diff --git a/TMessagesProj/build.gradle b/TMessagesProj/build.gradle index 54d0013308..e903287e1f 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 = "10.0.5" -def verCode = 1137 +def verName = "10.0.8" +def verCode = 1138 -def officialVer = "10.0.5" -def officialCode = 3804 +def officialVer = "10.0.8" +def officialCode = 3867 def serviceAccountCredentialsFile = rootProject.file("service_account_credentials.json") diff --git a/TMessagesProj/jni/CMakeLists.txt b/TMessagesProj/jni/CMakeLists.txt index 0474a9dc21..97aab362bd 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.45") +set(NATIVE_LIB "tmessages.46") #tmessages add_library(${NATIVE_LIB} SHARED diff --git a/TMessagesProj/jni/TgNetWrapper.cpp b/TMessagesProj/jni/TgNetWrapper.cpp index 7ef59aaa78..7e6e6848db 100644 --- a/TMessagesProj/jni/TgNetWrapper.cpp +++ b/TMessagesProj/jni/TgNetWrapper.cpp @@ -111,42 +111,38 @@ void sendRequest(JNIEnv *env, jclass c, jint instanceNum, jlong object, jobject DEBUG_REF("sendRequest onWriteToSocket"); onWriteToSocket = env->NewGlobalRef(onWriteToSocket); } - ConnectionsManager::getInstance(instanceNum).sendRequest(request, ([onComplete, instanceNum]( - TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime, int64_t msgId) { - TL_api_response *resp = (TL_api_response *) response; - jlong ptr = 0; - jint errorCode = 0; - jstring errorText = nullptr; - if (resp != nullptr) { - ptr = (jlong) resp->response.get(); - } else if (error != nullptr) { - errorCode = error->code; - const char *text = error->text.c_str(); - size_t size = error->text.size(); - if (check_utf8(text, size)) { - errorText = jniEnv[instanceNum]->NewStringUTF(text); - } else { - errorText = jniEnv[instanceNum]->NewStringUTF("UTF-8 ERROR"); - } - } - if (onComplete != nullptr) { - jniEnv[instanceNum]->CallVoidMethod(onComplete, jclass_RequestDelegateInternal_run, ptr, - errorCode, errorText, networkType, responseTime); - } - if (errorText != nullptr) { - jniEnv[instanceNum]->DeleteLocalRef(errorText); - } - }), ([onQuickAck, instanceNum] { - if (onQuickAck != nullptr) { - jniEnv[instanceNum]->CallVoidMethod(onQuickAck, jclass_QuickAckDelegate_run); - } - }), ([onWriteToSocket, instanceNum] { - if (onWriteToSocket != nullptr) { - jniEnv[instanceNum]->CallVoidMethod(onWriteToSocket, jclass_WriteToSocketDelegate_run); - } - }), (uint32_t) flags, (uint32_t) datacenterId, (ConnectionType) connetionType, immediate, token, - onComplete, onQuickAck, - onWriteToSocket); + ConnectionsManager::getInstance(instanceNum).sendRequest(request, ([onComplete, instanceNum](TLObject *response, TL_error *error, int32_t networkType, int64_t responseTime, int64_t msgId) { + TL_api_response *resp = (TL_api_response *) response; + jlong ptr = 0; + jint errorCode = 0; + jstring errorText = nullptr; + if (resp != nullptr) { + ptr = (jlong) resp->response.get(); + } else if (error != nullptr) { + errorCode = error->code; + const char *text = error->text.c_str(); + size_t size = error->text.size(); + if (check_utf8(text, size)) { + errorText = jniEnv[instanceNum]->NewStringUTF(text); + } else { + errorText = jniEnv[instanceNum]->NewStringUTF("UTF-8 ERROR"); + } + } + if (onComplete != nullptr) { + jniEnv[instanceNum]->CallVoidMethod(onComplete, jclass_RequestDelegateInternal_run, ptr, errorCode, errorText, networkType, responseTime, msgId); + } + if (errorText != nullptr) { + jniEnv[instanceNum]->DeleteLocalRef(errorText); + } + }), ([onQuickAck, instanceNum] { + if (onQuickAck != nullptr) { + jniEnv[instanceNum]->CallVoidMethod(onQuickAck, jclass_QuickAckDelegate_run); + } + }), ([onWriteToSocket, instanceNum] { + if (onWriteToSocket != nullptr) { + jniEnv[instanceNum]->CallVoidMethod(onWriteToSocket, jclass_WriteToSocketDelegate_run); + } + }), (uint32_t) flags, (uint32_t) datacenterId, (ConnectionType) connetionType, immediate, token, onComplete, onQuickAck, onWriteToSocket); } void cancelRequest(JNIEnv *env, jclass c, jint instanceNum, jint token, jboolean notifyServer) { diff --git a/TMessagesProj/jni/audio.c b/TMessagesProj/jni/audio.c index 55f1ec5a3b..dcceaa107c 100644 --- a/TMessagesProj/jni/audio.c +++ b/TMessagesProj/jni/audio.c @@ -7,6 +7,7 @@ #include #include #include "c_utils.h" +#include "libavformat/avformat.h" typedef struct { int version; @@ -687,3 +688,143 @@ JNIEXPORT jbyteArray Java_org_telegram_messenger_MediaController_getWaveform(JNI return result; } + +JNIEXPORT void JNICALL Java_org_telegram_ui_Stories_recorder_FfmpegAudioWaveformLoader_init(JNIEnv *env, jobject obj, jstring pathJStr, jint count) { + const char *path = (*env)->GetStringUTFChars(env, pathJStr, 0); + + // Initialize FFmpeg components + av_register_all(); + + AVFormatContext *formatContext = avformat_alloc_context(); + if (!formatContext) { + // Handle error + return; + } + + int res; + if ((res = avformat_open_input(&formatContext, path, NULL, NULL)) != 0) { + LOGD("avformat_open_input error %s", av_err2str(res)); + // Handle error + avformat_free_context(formatContext); + return; + } + + if (avformat_find_stream_info(formatContext, NULL) < 0) { + // Handle error + avformat_close_input(&formatContext); + return; + } + + AVCodec *codec = NULL; + int audioStreamIndex = av_find_best_stream(formatContext, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0); + if (audioStreamIndex < 0) { + LOGD("av_find_best_stream error %s", av_err2str(audioStreamIndex)); + // Handle error + avformat_close_input(&formatContext); + return; + } + + AVCodecContext *codecContext = avcodec_alloc_context3(codec); + avcodec_parameters_to_context(codecContext, formatContext->streams[audioStreamIndex]->codecpar); + + int64_t duration_in_microseconds = formatContext->duration; + double duration_in_seconds = (double)duration_in_microseconds / AV_TIME_BASE; + + if (avcodec_open2(codecContext, codec, NULL) < 0) { + // Handle error + avcodec_free_context(&codecContext); + avformat_close_input(&formatContext); + return; + } + + // Obtain the class and method to callback + jclass cls = (*env)->GetObjectClass(env, obj); + jmethodID mid = (*env)->GetMethodID(env, cls, "receiveChunk", "([SI)V"); + + AVFrame *frame = av_frame_alloc(); + AVPacket packet; + + int sampleRate = codecContext->sample_rate; // Sample rate from FFmpeg's codec context + int skip = 4; + int barWidth = (int) round((double) duration_in_seconds * sampleRate / count / (1 + skip)); // Assuming you have 'duration' and 'count' defined somewhere + + short peak = 0; + int currentCount = 0; + int index = 0; + int chunkIndex = 0; + short waveformChunkData[32]; // Allocate the chunk array + + while (av_read_frame(formatContext, &packet) >= 0) { + if (packet.stream_index == audioStreamIndex) { + // Decode the audio packet + int response = avcodec_send_packet(codecContext, &packet); + + while (response >= 0) { + response = avcodec_receive_frame(codecContext, frame); + if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) { + break; + } else if (response < 0) { + // Handle error + break; + } + + int16_t* samples = (int16_t*) frame->data[0]; + for (int i = 0; i < frame->nb_samples; i++) { + short value = samples[i]; // Read the 16-bit PCM sample + + if (currentCount >= barWidth) { + waveformChunkData[index - chunkIndex] = peak; + index++; + if (index - chunkIndex >= sizeof(waveformChunkData) / sizeof(short) || index >= count) { + jshortArray waveformData = (*env)->NewShortArray(env, sizeof(waveformChunkData) / sizeof(short)); + (*env)->SetShortArrayRegion(env, waveformData, 0, sizeof(waveformChunkData) / sizeof(short), waveformChunkData); + (*env)->CallVoidMethod(env, obj, mid, waveformData, sizeof(waveformChunkData) / sizeof(short)); + + // Reset the chunk data + memset(waveformChunkData, 0, sizeof(waveformChunkData)); + chunkIndex = index; + + // Delete local reference to avoid memory leak + (*env)->DeleteLocalRef(env, waveformData); + } + peak = 0; + currentCount = 0; + if (index >= count) { + break; + } + } + + if (peak < value) { + peak = value; + } + currentCount++; + + // Skip logic + i += skip; + if (i >= frame->nb_samples) { + break; + } + } + } + } + + av_packet_unref(&packet); + + if (index >= count) { + break; + } + + // Check for stopping flag + jfieldID fid = (*env)->GetFieldID(env, cls, "running", "Z"); + jboolean running = (*env)->GetBooleanField(env, obj, fid); + if (running == JNI_FALSE) { + break; + } + } + + av_frame_free(&frame); + avcodec_free_context(&codecContext); + avformat_close_input(&formatContext); + + (*env)->ReleaseStringUTFChars(env, pathJStr, path); +} \ No newline at end of file diff --git a/TMessagesProj/jni/tgnet/ConnectionSocket.cpp b/TMessagesProj/jni/tgnet/ConnectionSocket.cpp index 3cd73c8120..41d97c6a67 100644 --- a/TMessagesProj/jni/tgnet/ConnectionSocket.cpp +++ b/TMessagesProj/jni/tgnet/ConnectionSocket.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "ByteStream.h" #include "ConnectionSocket.h" @@ -29,6 +30,7 @@ #include "NativeByteBuffer.h" #include "BuffersStorage.h" #include "Connection.h" +#include #ifndef EPOLLRDHUP #define EPOLLRDHUP 0x2000 @@ -134,7 +136,7 @@ class TlsHello { grease[a] = (uint8_t) ((grease[a] & 0xf0) + 0x0A); } for (size_t i = 1; i < MAX_GREASE; i += 2) { - if (grease[i] == grease[i - 1]) { + if (grease[i] == grease[i + 1]) { grease[i] ^= 0x10; } } @@ -142,12 +144,13 @@ class TlsHello { struct Op { enum class Type { - String, Random, K, Zero, Domain, Grease, BeginScope, EndScope + String, Random, K, Zero, Domain, Grease, BeginScope, EndScope, Permutation }; Type type; size_t length; int seed; std::string data; + std::vector> entities; static Op string(const char str[], size_t len) { Op res; @@ -201,6 +204,14 @@ class TlsHello { res.type = Type::EndScope; return res; } + + static Op permutation(std::vector> entities) { + Op res; + res.type = Type::Permutation; + res.entities = std::move(entities); + return res; + } + }; static const TlsHello &getDefault() { @@ -213,32 +224,81 @@ class TlsHello { Op::random(32), Op::string("\x00\x20", 2), Op::grease(0), - Op::string("\x13\x01\x13\x02\x13\x03\xc0\x2b\xc0\x2f\xc0\x2c\xc0\x30\xcc\xa9\xcc\xa8\xc0\x13\xc0\x14\x00\x9c" - "\x00\x9d\x00\x2f\x00\x35\x01\x00\x01\x93", 34), + Op::string("\x13\x01\x13\x02\x13\x03\xc0\x2b\xc0\x2f\xc0\x2c\xc0\x30\xcc\xa9\xcc\xa8\xc0\x13\xc0\x14\x00\x9c\x00\x9d\x00\x2f\x00\x35\x01\x00\x01\x93", 34), Op::grease(2), - Op::string("\x00\x00\x00\x00", 4), - Op::begin_scope(), - Op::begin_scope(), - Op::string("\x00", 1), - Op::begin_scope(), - Op::domain(), - Op::end_scope(), - Op::end_scope(), - Op::end_scope(), - Op::string("\x00\x17\x00\x00\xff\x01\x00\x01\x00\x00\x0a\x00\x0a\x00\x08", 15), - Op::grease(4), - Op::string( - "\x00\x1d\x00\x17\x00\x18\x00\x0b\x00\x02\x01\x00\x00\x23\x00\x00\x00\x10\x00\x0e\x00\x0c\x02\x68\x32\x08" - "\x68\x74\x74\x70\x2f\x31\x2e\x31\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x0d\x00\x12\x00\x10\x04\x03\x08" - "\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x00\x12\x00\x00\x00\x33\x00\x2b\x00\x29", 75), - Op::grease(4), - Op::string("\x00\x01\x00\x00\x1d\x00\x20", 7), - Op::K(), - Op::string("\x00\x2d\x00\x02\x01\x01\x00\x2b\x00\x0b\x0a", 11), - Op::grease(6), - Op::string("\x03\x04\x03\x03\x03\x02\x03\x01\x00\x1b\x00\x03\x02\x00\x02", 15), + Op::string("\x00\x00", 2), + Op::permutation({ + { + Op::string("\x00\x00", 2), + Op::begin_scope(), + Op::begin_scope(), + Op::string("\x00", 1), + Op::begin_scope(), + Op::domain(), + Op::end_scope(), + Op::end_scope(), + Op::end_scope() + }, + { + Op::string( + "\x00\x05\x00\x05\x01\x00\x00\x00\x00", + 9) + }, + { + Op::string("\x00\x0a\x00\x0a\x00\x08", 6), + Op::grease(4), + Op::string("\x00\x1d\x00\x17\x00\x18", 6) + }, + { + Op::string("\x00\x0b\x00\x02\x01\x00", 6), + }, + { + Op::string( + "\x00\x0d\x00\x12\x00\x10\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01", + 22), + }, + { + Op::string( + "\x00\x10\x00\x0e\x00\x0c\x02\x68\x32\x08\x68\x74\x74\x70\x2f\x31\x2e\x31", + 18), + }, + { + Op::string("\x00\x12\x00\x00", 4) + }, + { + Op::string("\x00\x17\x00\x00", 4) + }, + { + Op::string("\x00\x1b\x00\x03\x02\x00\x02", 7) + }, + { + Op::string("\x00\x23\x00\x00", 4) + }, + { + Op::string("\x00\x2b\x00\x07\x06", 5), + Op::grease(6), + Op::string("\x03\x04\x03\x03", 4) + }, + { + Op::string("\x00\x2d\x00\x02\x01\x01", 6) + }, + { + Op::string("\x00\x33\x00\x2b\x00\x29", 6), + Op::grease(4), + Op::string("\x00\x01\x00\x00\x1d\x00\x20", 7), + Op::K() + }, + { + Op::string("\x44\x69\x00\x05\x00\x03\x02\x68\x32",9), + }, + { + Op::string("\xff\x01\x00\x01\x00", 5), + } + }), + Op::grease(3), - Op::string("\x00\x01\x00\x00\x15", 5)}; + Op::string("\x00\x01\x00\x00\x15", 5) + }; return res; }(); return result; @@ -319,6 +379,25 @@ class TlsHello { data[begin_offset + 1] = static_cast(size & 0xff); break; } + case Type::Permutation: { + std::vector> list = {}; + for (const auto &part : op.entities) { + list.push_back(part); + } + size_t size = list.size(); + for (int i = 0; i < size - 1; i++) { + int j = i + rand() % (size - i); + if (i != j) { + std::swap(list[i], list[j]); + } + } + for (const auto &part : list) { + for (const auto &op_local: part) { + writeOp(op_local, data, offset); + } + } + break; + } } } }; diff --git a/TMessagesProj/jni/tgnet/ConnectionsManager.cpp b/TMessagesProj/jni/tgnet/ConnectionsManager.cpp index 42edf148ea..809001ec71 100644 --- a/TMessagesProj/jni/tgnet/ConnectionsManager.cpp +++ b/TMessagesProj/jni/tgnet/ConnectionsManager.cpp @@ -968,7 +968,7 @@ void ConnectionsManager::onConnectionDataReceived(Connection *connection, Native } } else { if (delegate != nullptr) { - delegate->onUnparsedMessageReceived(0, data, connection->getConnectionType(), instanceNum); + delegate->onUnparsedMessageReceived(messageId, data, connection->getConnectionType(), instanceNum); } } } else { @@ -1126,7 +1126,7 @@ void ConnectionsManager::processServerResponse(TLObject *message, int64_t messag if (innerMessage->unparsedBody != nullptr) { if (LOGS_ENABLED) DEBUG_D("inner message %d id 0x%" PRIx64 " is unparsed", a, innerMessageId); if (delegate != nullptr) { - delegate->onUnparsedMessageReceived(0, innerMessage->unparsedBody.get(), connection->getConnectionType(), instanceNum); + delegate->onUnparsedMessageReceived(innerMessageId, innerMessage->unparsedBody.get(), connection->getConnectionType(), instanceNum); } } else { if (LOGS_ENABLED) DEBUG_D("inner message %d id 0x%" PRIx64 " process", a, innerMessageId); @@ -1641,7 +1641,7 @@ void ConnectionsManager::processServerResponse(TLObject *message, int64_t messag NativeByteBuffer *data = BuffersStorage::getInstance().getFreeBuffer(message->getObjectSize()); message->serializeToStream(data); data->position(0); - delegate->onUnparsedMessageReceived(0, data, connection->getConnectionType(), instanceNum); + delegate->onUnparsedMessageReceived(messageId, data, connection->getConnectionType(), instanceNum); data->reuse(); } } diff --git a/TMessagesProj/proguard-rules.pro b/TMessagesProj/proguard-rules.pro index 47550b4aec..cf81286654 100644 --- a/TMessagesProj/proguard-rules.pro +++ b/TMessagesProj/proguard-rules.pro @@ -43,6 +43,7 @@ -keep class com.google.android.exoplayer2.extractor.FlacStreamMetadata { *; } -keep class com.google.android.exoplayer2.metadata.flac.PictureFrame { *; } -keep class com.google.android.exoplayer2.decoder.SimpleDecoderOutputBuffer { *; } +-keep class org.telegram.ui.Stories.recorder.FfmpegAudioWaveformLoader { *; } -keep class org.dizitart.no2.** { *; } -keep class org.slf4j.** { *; } diff --git a/TMessagesProj/src/main/assets/arctic.attheme b/TMessagesProj/src/main/assets/arctic.attheme index b49ae58277..955d1070cf 100644 --- a/TMessagesProj/src/main/assets/arctic.attheme +++ b/TMessagesProj/src/main/assets/arctic.attheme @@ -130,7 +130,7 @@ chat_outViewsSelected=-1258291201 chat_outInstant=-1 actionBarDefaultSearchPlaceholder=-2005173381 chat_outForwardedNameText=-1 -dialogRoundCheckBox=-13653259 +dialogRoundCheckBox=-15033089 actionBarTabLine=-13655305 chats_nameMessageArchived_threeLines=-7237231 chat_outSiteNameText=-1 @@ -249,7 +249,7 @@ inappPlayerClose=-7563878 chat_outMediaIcon=-13332255 chat_outAudioCacheSeekbar=738197503 chats_sentClock=2066650878 -dialogFloatingButton=-12081419 +dialogFloatingButton=-15033089 chats_archiveBackground=-11294989 chat_inPreviewInstantText=-15299362 chat_outLoaderSelected=-1 diff --git a/TMessagesProj/src/main/assets/darkblue.attheme b/TMessagesProj/src/main/assets/darkblue.attheme index 7f42dfe751..e1b88a0e87 100644 --- a/TMessagesProj/src/main/assets/darkblue.attheme +++ b/TMessagesProj/src/main/assets/darkblue.attheme @@ -200,7 +200,7 @@ profile_tabSelector=350681087 actionBarDefaultSearchPlaceholder=2027746559 actionBarActionModeDefaultSelector=433780735 chat_outForwardedNameText=-7551233 -dialogRoundCheckBox=-10177041 +dialogRoundCheckBox=-15033089 chat_emojiPanelTrendingTitle=-1 actionBarTabLine=-10179073 chat_stickersHintPanel=-13484721 diff --git a/TMessagesProj/src/main/assets/day.attheme b/TMessagesProj/src/main/assets/day.attheme index a5aab99305..6944f3d313 100644 --- a/TMessagesProj/src/main/assets/day.attheme +++ b/TMessagesProj/src/main/assets/day.attheme @@ -141,7 +141,7 @@ chat_outViewsSelected=-3676417 chat_outInstant=-1 actionBarDefaultSearchPlaceholder=-2005173381 chat_outForwardedNameText=-1 -dialogRoundCheckBox=-13653259 +dialogRoundCheckBox=-15033089 actionBarTabLine=-13655305 chats_nameMessageArchived_threeLines=-7237231 chat_outSiteNameText=-1 @@ -271,7 +271,7 @@ chat_outMediaIcon=-14707997 chat_outAudioCacheSeekbar=738197503 chats_sentClock=2073474246 chat_inAudioSeekbar=-2762017 -dialogFloatingButton=-12081419 +dialogFloatingButton=-15033089 chats_archiveBackground=-11294989 chat_inPreviewInstantText=-15300135 chat_inViews=-274882397 diff --git a/TMessagesProj/src/main/assets/fonts/num.otf b/TMessagesProj/src/main/assets/fonts/num.otf new file mode 100644 index 0000000000..320ce49b2d Binary files /dev/null and b/TMessagesProj/src/main/assets/fonts/num.otf differ diff --git a/TMessagesProj/src/main/assets/night.attheme b/TMessagesProj/src/main/assets/night.attheme index 2d57cde4db..28941bb69a 100644 --- a/TMessagesProj/src/main/assets/night.attheme +++ b/TMessagesProj/src/main/assets/night.attheme @@ -213,7 +213,7 @@ actionBarDefaultSearchPlaceholder=-2097152001 profile_tabSelector=268435455 actionBarActionModeDefaultSelector=520093695 chat_outForwardedNameText=-5448193 -dialogRoundCheckBox=-10177041 +dialogRoundCheckBox=-15033089 chat_emojiPanelTrendingTitle=-1 actionBarTabLine=-12339201 chat_stickersHintPanel=-14606046 diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java index 55868a7efd..f838126028 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java @@ -1012,8 +1012,8 @@ private static void pruneOverlaps(ArrayList links) { } } - public static void fillStatusBarHeight(Context context) { - if (context == null || AndroidUtilities.statusBarHeight > 0) { + public static void fillStatusBarHeight(Context context, boolean force) { + if (context == null || (AndroidUtilities.statusBarHeight > 0 && !force)) { return; } AndroidUtilities.statusBarHeight = getStatusBarHeight(context); @@ -2287,10 +2287,8 @@ public static void checkDisplaySize(Context context, Configuration newConfigurat } roundMessageInset = dp(2); } + fillStatusBarHeight(context, true); if (BuildVars.LOGS_ENABLED) { - if (statusBarHeight == 0) { - fillStatusBarHeight(context); - } FileLog.e("density = " + density + " display size = " + displaySize.x + " " + displaySize.y + " " + displayMetrics.xdpi + "x" + displayMetrics.ydpi + ", screen layout: " + configuration.screenLayout + ", statusbar height: " + statusBarHeight + ", navbar height: " + navigationBarHeight); } ViewConfiguration vc = ViewConfiguration.get(context); @@ -5967,6 +5965,18 @@ public static Pair getImageOrientation(ExifInterface exif) { return new Pair<>(0, 0); } + public static void forEachViews(View view, Consumer consumer) { + if (view instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) view; + for (int i = 0; i < viewGroup.getChildCount(); i++) { + consumer.accept(view); + forEachViews(viewGroup.getChildAt(i), consumer); + } + } else { + consumer.accept(view); + } + } + public static void forEachViews(RecyclerView recyclerView, Consumer consumer) { for (int i = 0; i < recyclerView.getChildCount(); i++) { consumer.accept(recyclerView.getChildAt(i)); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java index 3ce295fe2f..65b020c313 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java @@ -182,6 +182,7 @@ public static void postInitApplication() { return; } applicationInited = true; + NativeLoader.initNativeLibs(ApplicationLoader.applicationContext); SharedConfig.loadConfig(); LocaleController.getInstance(); @@ -242,7 +243,7 @@ public void onReceive(Context context, Intent intent) { Runnable initRunnable = () -> loadAccount(finalA); if (finalA == UserConfig.selectedAccount) { initRunnable.run(); - ChatThemeController.init(); + ChatThemeController.getInstance(finalA); } else postRun.add(initRunnable); } @@ -280,6 +281,7 @@ public static void loadAccount(int account) { ContactsController.getInstance(account).checkAppAccount(); DownloadController.getInstance(account); }); +// BillingController.getInstance().startConnection(); } public ApplicationLoader() { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ChatObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/ChatObject.java index dbb810169e..a6d1c96138 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ChatObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ChatObject.java @@ -1665,6 +1665,16 @@ public static boolean isNotInChat(TLRPC.Chat chat) { return chat == null || chat instanceof TLRPC.TL_chatEmpty || chat instanceof TLRPC.TL_chatForbidden || chat instanceof TLRPC.TL_channelForbidden || chat.left || chat.kicked || chat.deactivated; } + public static boolean isInChat(TLRPC.Chat chat) { + if (chat == null || chat instanceof TLRPC.TL_chatEmpty || chat instanceof TLRPC.TL_chatForbidden || chat instanceof TLRPC.TL_channelForbidden) { + return false; + } + if (chat.left || chat.kicked || chat.deactivated) { + return false; + } + return true; + } + public static boolean canSendAsPeers(TLRPC.Chat chat) { return ChatObject.isChannel(chat) && chat.megagroup && (ChatObject.isPublic(chat) || chat.has_geo || chat.has_link); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ChatThemeController.java b/TMessagesProj/src/main/java/org/telegram/messenger/ChatThemeController.java index c0f445adcd..4648b8ba25 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ChatThemeController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ChatThemeController.java @@ -25,15 +25,20 @@ public class ChatThemeController extends BaseController { - private static final long reloadTimeoutMs = 2 * 60 * 60 * 1000; + private final long reloadTimeoutMs = 2 * 60 * 60 * 1000; public static volatile DispatchQueue chatThemeQueue = new DispatchQueue("chatThemeQueue"); - private static final HashMap themeIdWallpaperThumbMap = new HashMap<>(); - private static List allChatThemes; - private static volatile long themesHash; - private static volatile long lastReloadTimeMs; + private final HashMap themeIdWallpaperThumbMap = new HashMap<>(); + private List allChatThemes; + private volatile long themesHash; + private volatile long lastReloadTimeMs; - public static void init() { + private ChatThemeController(int num) { + super(num); + init(); + } + + private void init() { SharedPreferences preferences = getSharedPreferences(); themesHash = 0; lastReloadTimeMs = 0; @@ -54,20 +59,20 @@ public static void init() { } } - private static void preloadSticker(String emojicon) { + private void preloadSticker(String emojicon) { ImageReceiver imageReceiver = new ImageReceiver(); TLRPC.Document document = MediaDataController.getInstance(UserConfig.selectedAccount).getEmojiAnimatedSticker(emojicon); imageReceiver.setImage(ImageLocation.getForDocument(document), "50_50", null, null, null, 0); Emoji.preloadEmoji(emojicon); } - public static void requestAllChatThemes(final ResultCallback> callback, boolean withDefault) { + public void requestAllChatThemes(final ResultCallback> callback, boolean withDefault) { if (themesHash == 0 || lastReloadTimeMs == 0) { init(); } boolean needReload = System.currentTimeMillis() - lastReloadTimeMs > reloadTimeoutMs; - if (true || allChatThemes == null || allChatThemes.isEmpty() || needReload) { + if (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(() -> { @@ -90,7 +95,7 @@ public static void requestAllChatThemes(final ResultCallback> SerializedData data = new SerializedData(tlChatTheme.getObjectSize()); tlChatTheme.serializeToStream(data); editor.putString("theme_" + i, Utilities.bytesToHex(data.toByteArray())); - EmojiThemes chatTheme = new EmojiThemes(tlChatTheme, false); + EmojiThemes chatTheme = new EmojiThemes(currentAccount, tlChatTheme, false); chatTheme.preloadWallpaper(); chatThemes.add(chatTheme); } @@ -108,7 +113,7 @@ public static void requestAllChatThemes(final ResultCallback> } if (!isError) { if (withDefault && !chatThemes.get(0).showAsDefaultStub) { - chatThemes.add(0, EmojiThemes.createChatThemesDefault()); + chatThemes.add(0, EmojiThemes.createChatThemesDefault(currentAccount)); } for (EmojiThemes theme : chatThemes) { theme.initColors(); @@ -123,7 +128,7 @@ public static void requestAllChatThemes(final ResultCallback> if (allChatThemes != null && !allChatThemes.isEmpty()) { List chatThemes = new ArrayList<>(allChatThemes); if (withDefault && !chatThemes.get(0).showAsDefaultStub) { - chatThemes.add(0, EmojiThemes.createChatThemesDefault()); + chatThemes.add(0, EmojiThemes.createChatThemesDefault(currentAccount)); } for (EmojiThemes theme : chatThemes) { theme.initColors(); @@ -132,15 +137,15 @@ public static void requestAllChatThemes(final ResultCallback> } } - private static SharedPreferences getSharedPreferences() { - return ApplicationLoader.applicationContext.getSharedPreferences("chatthemeconfig", Context.MODE_PRIVATE); + private SharedPreferences getSharedPreferences() { + return ApplicationLoader.applicationContext.getSharedPreferences("chatthemeconfig_" + currentAccount, Context.MODE_PRIVATE); } - private static SharedPreferences getEmojiSharedPreferences() { + private SharedPreferences getEmojiSharedPreferences() { return ApplicationLoader.applicationContext.getSharedPreferences("chatthemeconfig_emoji", Context.MODE_PRIVATE); } - private static List getAllChatThemesFromPrefs() { + private List getAllChatThemesFromPrefs() { SharedPreferences preferences = getSharedPreferences(); int count = preferences.getInt("count", 0); List themes = new ArrayList<>(count); @@ -150,7 +155,7 @@ private static List getAllChatThemesFromPrefs() { try { TLRPC.TL_theme chatTheme = TLRPC.Theme.TLdeserialize(serializedData, serializedData.readInt32(true), true); if (chatTheme != null) { - themes.add(new EmojiThemes(chatTheme, false)); + themes.add(new EmojiThemes(currentAccount, chatTheme, false)); } } catch (Throwable e) { FileLog.e(e); @@ -159,7 +164,7 @@ private static List getAllChatThemesFromPrefs() { return themes; } - public static void requestChatTheme(final String emoticon, final ResultCallback callback) { + public void requestChatTheme(final String emoticon, final ResultCallback callback) { if (TextUtils.isEmpty(emoticon)) { callback.onComplete(null); return; @@ -203,10 +208,6 @@ public static ChatThemeController getInstance(int accountNum) { private final LongSparseArray dialogEmoticonsMap = new LongSparseArray<>(); - public ChatThemeController(int num) { - super(num); - } - public static boolean equals(TLRPC.WallPaper wallPaper, TLRPC.WallPaper oldWallpaper) { if (wallPaper == null && oldWallpaper == null) { return true; @@ -313,7 +314,7 @@ public TLRPC.WallPaper getDialogWallpaper(long dialogId) { return null; } - public static void preloadAllWallpaperImages(boolean isDark) { + public void preloadAllWallpaperImages(boolean isDark) { for (EmojiThemes chatTheme : allChatThemes) { TLRPC.TL_theme theme = chatTheme.getTlTheme(isDark ? 1 : 0); if (theme == null) { @@ -327,7 +328,7 @@ public static void preloadAllWallpaperImages(boolean isDark) { } } - public static void preloadAllWallpaperThumbs(boolean isDark) { + public void preloadAllWallpaperThumbs(boolean isDark) { for (EmojiThemes chatTheme : allChatThemes) { TLRPC.TL_theme theme = chatTheme.getTlTheme(isDark ? 1 : 0); if (theme == null) { @@ -345,15 +346,15 @@ public static void preloadAllWallpaperThumbs(boolean isDark) { } } - public static void clearWallpaperImages() { + public void clearWallpaperImages() { } - public static void clearWallpaperThumbImages() { + public void clearWallpaperThumbImages() { themeIdWallpaperThumbMap.clear(); } - public static void getWallpaperBitmap(long themeId, ResultCallback callback) { + public void getWallpaperBitmap(long themeId, ResultCallback callback) { if (themesHash == 0) { callback.onComplete(null); return; @@ -377,11 +378,11 @@ public static void getWallpaperBitmap(long themeId, ResultCallback callb }); } - private static File getPatternFile(long themeId) { + private File getPatternFile(long themeId) { return new File(ApplicationLoader.getFilesDirFixed(), String.format(Locale.US, "%d_%d.jpg", themeId, themesHash)); } - public static void saveWallpaperBitmap(Bitmap bitmap, long themeId) { + public void saveWallpaperBitmap(Bitmap bitmap, long themeId) { File file = getPatternFile(themeId); chatThemeQueue.postRunnable(() -> { try { @@ -394,7 +395,7 @@ public static void saveWallpaperBitmap(Bitmap bitmap, long themeId) { }); } - public static Bitmap getWallpaperThumbBitmap(long themeId) { + public Bitmap getWallpaperThumbBitmap(long themeId) { return themeIdWallpaperThumbMap.get(themeId); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/DatabaseMigrationHelper.java b/TMessagesProj/src/main/java/org/telegram/messenger/DatabaseMigrationHelper.java index 9011aff2f0..b63f4a9bdd 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/DatabaseMigrationHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/DatabaseMigrationHelper.java @@ -1340,6 +1340,36 @@ public static int migrate(MessagesStorage messagesStorage, int version) throws E version = 129; } + if (version == 129) { + database.executeFast("CREATE INDEX IF NOT EXISTS stickers_featured_emoji_index ON stickers_featured(emoji);").stepThis().dispose(); + + database.executeFast("PRAGMA user_version = 130").stepThis().dispose(); + version = 130; + } + + if (version == 130) { + database.executeFast("DROP TABLE archived_stories").stepThis().dispose(); + database.executeFast("ALTER TABLE profile_stories ADD COLUMN type INTEGER default 0").stepThis().dispose(); + + database.executeFast("PRAGMA user_version = 131").stepThis().dispose(); + version = 131; + } + + if (version == 131) { + database.executeFast("ALTER TABLE stories DROP COLUMN local_path").stepThis().dispose(); + database.executeFast("ALTER TABLE stories DROP COLUMN local_thumb_path").stepThis().dispose(); + + database.executeFast("PRAGMA user_version = 132").stepThis().dispose(); + version = 132; + } + + if (version == 132) { + database.executeFast("CREATE TABLE unconfirmed_auth (data BLOB);").stepThis().dispose(); + + database.executeFast("PRAGMA user_version = 133").stepThis().dispose(); + version = 133; + } + return version; } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java index dd3391301c..5812c17f01 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java @@ -271,6 +271,7 @@ public interface FileLoadOperationDelegate { void didChangedLoadProgress(FileLoadOperation operation, long uploadedSize, long totalSize); void saveFilePath(FilePathDatabase.PathData pathSaveData, File cacheFileFinal); boolean hasAnotherRefOnFile(String path); + boolean isLocallyCreatedFile(String path); } private void updateParams() { @@ -952,7 +953,7 @@ public boolean start(final FileLoadOperationStream stream, final long streamOffs } } boolean finalFileExist = cacheFileFinal.exists(); - if (finalFileExist && (parentObject instanceof TLRPC.TL_theme || (totalBytesCount != 0 && !ungzip && totalBytesCount != cacheFileFinal.length()))) { + if (finalFileExist && (parentObject instanceof TLRPC.TL_theme || (totalBytesCount != 0 && !ungzip && totalBytesCount != cacheFileFinal.length())) && !delegate.isLocallyCreatedFile(cacheFileFinal.toString())) { if (BuildVars.LOGS_ENABLED) { FileLog.d("debug_loading: delete existing file cause file size mismatch " + cacheFileFinal.getName() + " totalSize=" + totalBytesCount + " existingFileSize=" + cacheFileFinal.length()); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java index fd7eb7883b..11cc6ad844 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java @@ -11,8 +11,6 @@ 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; @@ -124,6 +122,41 @@ public DispatchQueue getFileLoaderQueue() { return fileLoaderQueue; } + public void setLocalPathTo(TLObject attach, String attachPath) { + long documentId = 0; + int dcId = 0; + int type = 0; + if (attach instanceof TLRPC.Document) { + TLRPC.Document document = (TLRPC.Document) attach; + if (document.key != null) { + type = MEDIA_DIR_CACHE; + } else { + if (MessageObject.isVoiceDocument(document)) { + type = MEDIA_DIR_AUDIO; + } else if (MessageObject.isVideoDocument(document)) { + type = MEDIA_DIR_VIDEO; + } else { + type = MEDIA_DIR_DOCUMENT; + } + } + documentId = document.id; + dcId = document.dc_id; + filePathDatabase.putPath(documentId, dcId, type, FilePathDatabase.FLAG_LOCALLY_CREATED, attachPath); + } else if (attach instanceof TLRPC.PhotoSize) { + TLRPC.PhotoSize photoSize = (TLRPC.PhotoSize) attach; + if (photoSize instanceof TLRPC.TL_photoStrippedSize || photoSize instanceof TLRPC.TL_photoPathSize) { + return; + } else if (photoSize.location == null || photoSize.location.key != null || photoSize.location.volume_id == Integer.MIN_VALUE && photoSize.location.local_id < 0 || photoSize.size < 0) { + type = MEDIA_DIR_CACHE; + } else { + type = MEDIA_DIR_IMAGE; + } + documentId = photoSize.location.volume_id; + dcId = photoSize.location.dc_id + (photoSize.location.local_id << 16); + filePathDatabase.putPath(documentId, dcId, type, FilePathDatabase.FLAG_LOCALLY_CREATED, attachPath); + } + } + public interface FileLoaderDelegate { void fileUploadProgressChanged(FileUploadOperation operation, String location, long uploadedSize, long totalSize, boolean isEncrypted); @@ -939,13 +972,18 @@ public void didChangedLoadProgress(FileLoadOperation operation, long uploadedSiz @Override public void saveFilePath(FilePathDatabase.PathData pathSaveData, File cacheFileFinal) { - getFileDatabase().putPath(pathSaveData.id, pathSaveData.dc, pathSaveData.type, cacheFileFinal != null ? cacheFileFinal.toString() : null); + getFileDatabase().putPath(pathSaveData.id, pathSaveData.dc, pathSaveData.type, 0, cacheFileFinal != null ? cacheFileFinal.toString() : null); } @Override public boolean hasAnotherRefOnFile(String path) { return getFileDatabase().hasAnotherRefOnFile(path); } + + @Override + public boolean isLocallyCreatedFile(String path) { + return getFileDatabase().isLocallyCreated(path); + } }; operation.setDelegate(fileLoadOperationDelegate); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FilePathDatabase.java b/TMessagesProj/src/main/java/org/telegram/messenger/FilePathDatabase.java index e5b7f4b11f..fcae4b5dfa 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FilePathDatabase.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FilePathDatabase.java @@ -17,6 +17,8 @@ public class FilePathDatabase { + public final static int FLAG_LOCALLY_CREATED = 1; //file is locally created, skip file size check in FileLoader + private DispatchQueue dispatchQueue; private final int currentAccount; @@ -24,7 +26,7 @@ public class FilePathDatabase { private File cacheFile; private File shmCacheFile; - private final static int LAST_DB_VERSION = 4; + private final static int LAST_DB_VERSION = 7; private final static String DATABASE_NAME = "file_to_path"; private final static String DATABASE_BACKUP_NAME = "file_to_path_backup"; @@ -58,7 +60,7 @@ public void createDatabase(int tryCount, boolean fromBackup) { database.executeFast("PRAGMA temp_store = MEMORY").stepThis().dispose(); if (createTable) { - database.executeFast("CREATE TABLE paths(document_id INTEGER, dc_id INTEGER, type INTEGER, path TEXT, PRIMARY KEY(document_id, dc_id, type));").stepThis().dispose(); + database.executeFast("CREATE TABLE paths(document_id INTEGER, dc_id INTEGER, type INTEGER, path TEXT, flags INTEGER, PRIMARY KEY(document_id, dc_id, type));").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS path_in_paths ON paths(path);").stepThis().dispose(); database.executeFast("CREATE TABLE paths_by_dialog_id(path TEXT PRIMARY KEY, dialog_id INTEGER, message_id INTEGER, message_type INTEGER);").stepThis().dispose(); @@ -111,6 +113,15 @@ private void migrateDatabase(int version) throws SQLiteException { database.executeFast("ALTER TABLE paths_by_dialog_id ADD COLUMN message_id INTEGER default 0").stepThis().dispose(); database.executeFast("ALTER TABLE paths_by_dialog_id ADD COLUMN message_type INTEGER default 0").stepThis().dispose(); database.executeFast("PRAGMA user_version = " + 4).stepThis().dispose(); + version = 4; + } + if (version == 4 || version == 5 || version == 6) { + try { + database.executeFast("ALTER TABLE paths ADD COLUMN flags INTEGER default 0").stepThis().dispose(); + } catch (Throwable ignore) { + FileLog.e(ignore); + } + database.executeFast("PRAGMA user_version = " + 7).stepThis().dispose(); } } @@ -231,7 +242,7 @@ public void ensureDatabaseCreated() { } } - public void putPath(long id, int dc, int type, String path) { + public void putPath(long id, int dc, int type, int flags, String path) { postRunnable(() -> { if (BuildVars.DEBUG_VERSION) { FileLog.d("put file path id=" + id + " dc=" + dc + " type=" + type + " path=" + path); @@ -248,12 +259,13 @@ public void putPath(long id, int dc, int type, String path) { deleteState.bindString(1, path); deleteState.step(); - state = database.executeFast("REPLACE INTO paths VALUES(?, ?, ?, ?)"); + state = database.executeFast("REPLACE INTO paths VALUES(?, ?, ?, ?, ?)"); state.requery(); state.bindLong(1, id); state.bindInteger(2, dc); state.bindInteger(3, type); state.bindString(4, path); + state.bindInteger(5, flags); state.step(); state.dispose(); } else { @@ -485,6 +497,31 @@ private void ensureQueueExist() { } } + public boolean isLocallyCreated(String path) { + CountDownLatch syncLatch = new CountDownLatch(1); + boolean[] res = new boolean[]{false}; + postRunnable(() -> { + ensureDatabaseCreated(); + try { + SQLiteCursor cursor = database.queryFinalized("SELECT flags FROM paths WHERE path = '" + path + "'"); + if (cursor.next()) { + res[0] = (cursor.intValue(0) & FLAG_LOCALLY_CREATED) != 0; + } + } catch (Exception e) { + FileLog.e(e); + } finally { + syncLatch.countDown(); + } + }); + + try { + syncLatch.await(); + } catch (InterruptedException e) { + FileLog.e(e); + } + return res[0]; + } + public static class PathData { public final long id; public final int dc; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java index 0c3c5c7534..faef660e21 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java @@ -912,7 +912,35 @@ private boolean onRequestComplete(String locationKey, String parentKey, TLObject } } else if (response instanceof TLRPC.TL_help_appUpdate) { TLRPC.TL_help_appUpdate appUpdate = (TLRPC.TL_help_appUpdate) response; - result = getFileReference(appUpdate.document, requester.location, needReplacement, locationReplacement); + try { + SharedConfig.pendingAppUpdate = appUpdate; + SharedConfig.saveConfig(); + } catch (Exception e) { + FileLog.e(e); + } + try { + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.appUpdateAvailable); + } catch (Exception e) { + FileLog.e(e); + } + try { + if (appUpdate.document != null) { + result = appUpdate.document.file_reference; + TLRPC.TL_inputDocumentFileLocation location = new TLRPC.TL_inputDocumentFileLocation(); + location.id = appUpdate.document.id; + location.access_hash = appUpdate.document.access_hash; + location.file_reference = appUpdate.document.file_reference; + location.thumb_size = ""; + locationReplacement = new TLRPC.InputFileLocation[1]; + locationReplacement[0] = location; + } + } catch (Exception e) { + result = null; + FileLog.e(e); + } + if (result == null) { + result = getFileReference(appUpdate.document, requester.location, needReplacement, locationReplacement); + } if (result == null) { result = getFileReference(appUpdate.sticker, requester.location, needReplacement, locationReplacement); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileStreamLoadOperation.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileStreamLoadOperation.java index b99a1a45af..dbe256405c 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileStreamLoadOperation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileStreamLoadOperation.java @@ -104,6 +104,9 @@ public long open(DataSpec dataSpec) throws IOException { try { file = new RandomAccessFile(currentFile, "r"); file.seek(currentOffset); + if (loadOperation.isFinished()) { + bytesRemaining = currentFile.length() - currentOffset; + } } catch (Throwable e) { } } @@ -163,6 +166,9 @@ public int read(byte[] buffer, int offset, int readLength) throws IOException { try { file = new RandomAccessFile(currentFile, "r"); file.seek(currentOffset); + if (loadOperation.isFinished()) { + bytesRemaining = currentFile.length() - currentOffset; + } } catch (Throwable e) { } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ImageReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/ImageReceiver.java index 1e1a1b8d0a..4534c43d45 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ImageReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ImageReceiver.java @@ -129,6 +129,10 @@ public BitmapHolder(Bitmap b) { recycleOnRelease = true; } + public String getKey() { + return key; + } + public int getWidth() { return bitmap != null ? bitmap.getWidth() : 0; } @@ -3096,7 +3100,7 @@ public void setCurrentTime(long time) { public void setFileLoadingPriority(int fileLoadingPriority) { if (this.fileLoadingPriority != fileLoadingPriority) { this.fileLoadingPriority = fileLoadingPriority; - if (attachedToWindow) { + if (attachedToWindow && hasImageSet()) { ImageLoader.getInstance().changeFileLoadingPriorityForImageReceiver(this); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java index 59ec79ba14..e9e4cfb304 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java @@ -127,6 +127,8 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, public native byte[] getWaveform2(short[] array, int length); + public static native byte[] getMp3Waveform(String path, int samplesCount); + public boolean isBuffering() { if (audioPlayer != null) { return audioPlayer.isBuffering(); @@ -300,8 +302,7 @@ public boolean isEmpty() { Math.abs(vignetteValue) < 0.1f && Math.abs(grainValue) < 0.1f && blurType == 0 && - Math.abs(sharpenValue) < 0.1f && - Math.abs(blurExcludeSize) < 0.1f + Math.abs(sharpenValue) < 0.1f ); } } @@ -5268,7 +5269,7 @@ private boolean convertVideo(final VideoConvertMessage convertMessage) { // } // boolean needCompress = avatarStartTime != -1 || info.cropState != null || info.mediaEntities != null || info.paintPath != null || info.filterState != null || - resultWidth != originalWidth || resultHeight != originalHeight || rotationValue != 0 || info.roundVideo || startTime != -1; + resultWidth != originalWidth || resultHeight != originalHeight || rotationValue != 0 || info.roundVideo || startTime != -1 || !info.mixedSoundInfos.isEmpty(); SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("videoconvert", Activity.MODE_PRIVATE); @@ -5305,7 +5306,7 @@ public void didWriteData(long availableSize, float progress) { info.videoConvertFirstWrite = true; MediaCodecVideoConvertor videoConvertor = new MediaCodecVideoConvertor(); - boolean error = videoConvertor.convertVideo(videoPath, cacheFile, + MediaCodecVideoConvertor.ConvertVideoParams convertVideoParams = MediaCodecVideoConvertor.ConvertVideoParams.of(videoPath, cacheFile, rotationValue, isSecret, originalWidth, originalHeight, resultWidth, resultHeight, @@ -5314,6 +5315,7 @@ public void didWriteData(long availableSize, float progress) { needCompress, duration, info.filterState, info.paintPath, + info.blurPath, info.mediaEntities, info.isPhoto, info.cropState, @@ -5324,8 +5326,9 @@ public void didWriteData(long availableSize, float progress) { info.muted, info.isStory, info.hdrInfo, - info.parts - ); + info.parts); + convertVideoParams.soundInfos.addAll(info.mixedSoundInfos); + boolean error = videoConvertor.convertVideo(convertVideoParams); boolean canceled = info.canceled; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java index d57c92d106..dcaf06b98e 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java @@ -34,7 +34,6 @@ import android.text.style.CharacterStyle; import android.text.style.URLSpan; import android.text.util.Linkify; -import android.util.Log; import android.util.Pair; import android.util.SparseArray; import android.widget.Toast; @@ -105,7 +104,8 @@ public class MediaDataController extends BaseController { ATTACH_MENU_BOT_COLOR_LIGHT_ICON = "light_icon", ATTACH_MENU_BOT_COLOR_LIGHT_TEXT = "light_text", ATTACH_MENU_BOT_COLOR_DARK_ICON = "dark_icon", - ATTACH_MENU_BOT_COLOR_DARK_TEXT = "dark_text"; + ATTACH_MENU_BOT_COLOR_DARK_TEXT = "dark_text", + ATTACH_MENU_BOT_SIDE_MENU = "android_side_menu_static"; private static Pattern BOLD_PATTERN = Pattern.compile("\\*\\*(.+?)\\*\\*"), ITALIC_PATTERN = Pattern.compile("__(.+?)__"), @@ -183,6 +183,8 @@ public MediaDataController(int num) { loadAvatarConstructor(false); loadAvatarConstructor(true); ringtoneDataStore = new RingtoneDataStore(currentAccount); + + menuBotsUpdateDate = getMessagesController().getMainSettings().getInt("menuBotsUpdateDate", 0); } public static final int TYPE_IMAGE = 0; @@ -199,6 +201,7 @@ public MediaDataController(int num) { private long menuBotsUpdateHash; private TLRPC.TL_attachMenuBots attachMenuBots = new TLRPC.TL_attachMenuBots(); private boolean isLoadingMenuBots; + private boolean menuBotsUpdatedLocal; private int menuBotsUpdateDate; private int reactionsUpdateHash; @@ -375,8 +378,8 @@ public void checkReactions() { } } - public void checkMenuBots() { - if (!isLoadingMenuBots && Math.abs(System.currentTimeMillis() / 1000 - menuBotsUpdateDate) >= 60 * 60) { + public void checkMenuBots(boolean atStart) { + if (!isLoadingMenuBots && (atStart && !menuBotsUpdatedLocal || Math.abs(System.currentTimeMillis() / 1000 - menuBotsUpdateDate) >= 60 * 60)) { loadAttachMenuBots(true, false); } } @@ -404,6 +407,9 @@ public TLRPC.TL_attachMenuBots getAttachMenuBots() { } public void loadAttachMenuBots(boolean cache, boolean force) { + loadAttachMenuBots(cache, force, null); + } + public void loadAttachMenuBots(boolean cache, boolean force, Runnable onDone) { isLoadingMenuBots = true; if (cache) { getMessagesStorage().getStorageQueue().postRunnable(() -> { @@ -425,6 +431,13 @@ public void loadAttachMenuBots(boolean cache, boolean force) { hash = c.longValue(1); date = c.intValue(2); } + if (bots != null) { + ArrayList usersToLoad = new ArrayList<>(); + for (int i = 0; i < bots.bots.size(); i++) { + usersToLoad.add(bots.bots.get(i).bot_id); + } + bots.users.addAll(getMessagesStorage().getUsers(usersToLoad)); + } } catch (Exception e) { FileLog.e(e, false); } finally { @@ -445,6 +458,9 @@ public void loadAttachMenuBots(boolean cache, boolean force) { TLRPC.TL_attachMenuBots r = (TLRPC.TL_attachMenuBots) response; processLoadedMenuBots(r, r.hash, date, false); } + if (onDone != null) { + AndroidUtilities.runOnUIThread(onDone); + } }); } } @@ -454,19 +470,35 @@ public void processLoadedMenuBots(TLRPC.TL_attachMenuBots bots, long hash, int d attachMenuBots = bots; menuBotsUpdateHash = hash; } - menuBotsUpdateDate = date; + getMessagesController().getMainSettings().edit().putInt("menuBotsUpdateDate", menuBotsUpdateDate = date).commit(); + menuBotsUpdatedLocal = true; + boolean forceReload = false; if (bots != null) { + if (!cache) { + getMessagesStorage().putUsersAndChats(bots.users, null, true, false); + } getMessagesController().putUsers(bots.users, cache); AndroidUtilities.runOnUIThread(() -> NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.attachMenuBotsDidLoad)); + for (int i = 0; i < bots.bots.size(); i++) { + if (bots.bots.get(i) instanceof TLRPC.TL_attachMenuBot_layer162) { + bots.bots.get(i).show_in_attach_menu = true; + forceReload = true; + } + } } if (!cache) { putMenuBotsToCache(bots, hash, date); - } else if (Math.abs(System.currentTimeMillis() / 1000 - date) >= 60 * 60) { + } else if (forceReload || Math.abs(System.currentTimeMillis() / 1000 - date) >= 60 * 60) { loadAttachMenuBots(false, true); } } + public void updateAttachMenuBotsInCache() { + if (getAttachMenuBots() != null) { + putMenuBotsToCache(getAttachMenuBots(), menuBotsUpdateHash, menuBotsUpdateDate); + } + } private void putMenuBotsToCache(TLRPC.TL_attachMenuBots bots, long hash, int date) { getMessagesStorage().getStorageQueue().postRunnable(() -> { try { @@ -675,18 +707,18 @@ public void preloadDefaultReactions() { int N = Math.min(arrayList.size(), 10); for (int i = 0; i < N; i++) { TLRPC.TL_availableReaction reaction = arrayList.get(i); - preloadImage(ImageLocation.getForDocument(reaction.activate_animation)); - preloadImage(ImageLocation.getForDocument(reaction.appear_animation)); + preloadImage(ImageLocation.getForDocument(reaction.activate_animation), FileLoader.PRIORITY_LOW); + preloadImage(ImageLocation.getForDocument(reaction.appear_animation), FileLoader.PRIORITY_LOW); } for (int i = 0; i < N; i++) { TLRPC.TL_availableReaction reaction = arrayList.get(i); - preloadImage(ImageLocation.getForDocument(reaction.effect_animation)); + preloadImage(ImageLocation.getForDocument(reaction.effect_animation), FileLoader.PRIORITY_LOW); } } - private void preloadImage(ImageLocation location) { - getFileLoader().loadFile(location, null, null, FileLoader.PRIORITY_LOW, FileLoader.PRELOAD_CACHE_TYPE); + public void preloadImage(ImageLocation location, int priority) { + getFileLoader().loadFile(location, null, null, priority, FileLoader.PRELOAD_CACHE_TYPE); } public void preloadImage(ImageReceiver imageReceiver, ImageLocation location, String filter) { @@ -1231,14 +1263,21 @@ private void fetchStickerSetInternal(long id, Integer hash, Utilities.Callback2< }); } + private final HashSet loadingStickerSets = new HashSet<>(); + private void fetchStickerSetInternal(TLRPC.InputStickerSet inputStickerSet, Utilities.Callback2 onDone) { if (onDone == null) { return; } + if (loadingStickerSets.contains(inputStickerSet)) { + return; + } + loadingStickerSets.add(inputStickerSet); TLRPC.TL_messages_getStickerSet req = new TLRPC.TL_messages_getStickerSet(); req.stickerset = inputStickerSet; getConnectionsManager().sendRequest(req, (response, error) -> { AndroidUtilities.runOnUIThread(() -> { + loadingStickerSets.remove(inputStickerSet); // if (error != null && "".equals(error.text)) { // onDone.run(true, null); // } else @@ -1457,6 +1496,16 @@ public static TLRPC.TL_attachMenuBotIcon getAnimatedAttachMenuBotIcon(@NonNull T return null; } + @Nullable + public static TLRPC.TL_attachMenuBotIcon getSideMenuBotIcon(@NonNull TLRPC.TL_attachMenuBot bot) { + for (TLRPC.TL_attachMenuBotIcon icon : bot.icons) { + if (icon.name.equals(ATTACH_MENU_BOT_SIDE_MENU)) { + return icon; + } + } + return null; + } + @Nullable public static TLRPC.TL_attachMenuBotIcon getStaticAttachMenuBotIcon(@NonNull TLRPC.TL_attachMenuBot bot) { for (TLRPC.TL_attachMenuBotIcon icon : bot.icons) { @@ -2322,7 +2371,7 @@ public void checkGenericAnimations() { processLoadedDiceStickers(getUserConfig().genericAnimationsStickerPack, false, stickerSet, false, (int) (System.currentTimeMillis() / 1000)); for (int i = 0; i < stickerSet.documents.size(); i++) { if (currentAccount == UserConfig.selectedAccount) { - preloadImage(ImageLocation.getForDocument(stickerSet.documents.get(i))); + preloadImage(ImageLocation.getForDocument(stickerSet.documents.get(i)), FileLoader.PRIORITY_LOW); } } } @@ -7138,7 +7187,7 @@ public void preloadPremiumPreviewStickers() { })); } - public void chekAllMedia(boolean force) { + public void checkAllMedia(boolean force) { if (force) { reactionsUpdateDate = 0; loadFeaturedDate[0] = 0; @@ -7150,7 +7199,7 @@ public void chekAllMedia(boolean force) { checkFeaturedStickers(); checkFeaturedEmoji(); checkReactions(); - checkMenuBots(); + checkMenuBots(true); checkPremiumPromo(); checkPremiumGiftStickers(); checkGenericAnimations(); @@ -7783,13 +7832,13 @@ public void loadEmojiThemes() { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("emojithemes_config_" + currentAccount, Context.MODE_PRIVATE); int count = preferences.getInt("count", 0); ArrayList previewItems = new ArrayList<>(); - previewItems.add(new ChatThemeBottomSheet.ChatThemeItem(EmojiThemes.createHomePreviewTheme())); + previewItems.add(new ChatThemeBottomSheet.ChatThemeItem(EmojiThemes.createHomePreviewTheme(currentAccount))); for (int i = 0; i < count; ++i) { String value = preferences.getString("theme_" + i, ""); SerializedData serializedData = new SerializedData(Utilities.hexToBytes(value)); try { TLRPC.TL_theme theme = TLRPC.Theme.TLdeserialize(serializedData, serializedData.readInt32(true), true); - EmojiThemes fullTheme = EmojiThemes.createPreviewFullTheme(theme); + EmojiThemes fullTheme = EmojiThemes.createPreviewFullTheme(currentAccount, theme); if (fullTheme.items.size() >= 4) { previewItems.add(new ChatThemeBottomSheet.ChatThemeItem(fullTheme)); } @@ -7828,10 +7877,10 @@ public void generateEmojiPreviewThemes(final ArrayList emojiPrev if (!emojiPreviewThemes.isEmpty()) { final ArrayList previewItems = new ArrayList<>(); - previewItems.add(new ChatThemeBottomSheet.ChatThemeItem(EmojiThemes.createHomePreviewTheme())); + previewItems.add(new ChatThemeBottomSheet.ChatThemeItem(EmojiThemes.createHomePreviewTheme(currentAccount))); for (int i = 0; i < emojiPreviewThemes.size(); i++) { TLRPC.TL_theme theme = emojiPreviewThemes.get(i); - EmojiThemes chatTheme = EmojiThemes.createPreviewFullTheme(theme); + EmojiThemes chatTheme = EmojiThemes.createPreviewFullTheme(currentAccount, theme); ChatThemeBottomSheet.ChatThemeItem item = new ChatThemeBottomSheet.ChatThemeItem(chatTheme); if (chatTheme.items.size() >= 4) { previewItems.add(item); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java index 5eaf28f15e..f842cdccba 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java @@ -45,6 +45,7 @@ import org.telegram.ui.Cells.ChatMessageCell; import org.telegram.ui.Components.AnimatedEmojiDrawable; import org.telegram.ui.Components.AnimatedEmojiSpan; +import org.telegram.ui.Components.ColoredImageSpan; import org.telegram.ui.Components.Forum.ForumBubbleDrawable; import org.telegram.ui.Components.Forum.ForumUtilities; import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; @@ -182,6 +183,7 @@ public class MessageObject { public StringBuilder botButtonsLayout; public boolean isRestrictedMessage; public long loadedFileSize; + public boolean forceExpired; public boolean isSpoilersRevealed = NekoConfig.showSpoilersDirectly.Bool(); public boolean isMediaSpoilersRevealed; @@ -243,6 +245,7 @@ public class MessageObject { public int lastLineWidth; public int textWidth; public int textHeight; + public int captionHeight; public boolean hasRtl; public float textXOffset; @@ -416,7 +419,7 @@ public int getEmojiOnlyCount() { public boolean hasMediaSpoilers() { if (NekoConfig.showSpoilersDirectly.Bool()) return false; - return messageOwner.media != null && messageOwner.media.spoiler; + return messageOwner.media != null && messageOwner.media.spoiler || needDrawBluredPreview(); } public boolean shouldDrawReactionsInLayout() { @@ -3612,8 +3615,11 @@ private void updateMessageText(AbstractMap users, AbstractMap< messageText = AndroidUtilities.replaceTags(LocaleController.formatString("AutoDeleteGlobalActionFromYou", R.string.AutoDeleteGlobalActionFromYou, LocaleController.formatTTLString(action.period))); } else { TLObject object = null; - if (users != null) { - users.get(action.auto_setting_from); + if (sUsers != null) { + object = sUsers.get(action.auto_setting_from); + } + if (object == null && users != null) { + object = users.get(action.auto_setting_from); } if (object == null && chats != null) { object = chats.get(action.auto_setting_from); @@ -3979,7 +3985,7 @@ private void updateMessageText(AbstractMap users, AbstractMap< } else { messageText = LocaleController.getString("AttachPhoto", R.string.AttachPhoto); } - } else if (isVideo() || getMedia(messageOwner) instanceof TLRPC.TL_messageMediaDocument && getDocument() instanceof TLRPC.TL_documentEmpty && getMedia(messageOwner).ttl_seconds != 0) { + } else if (isVideo() || getMedia(messageOwner) instanceof TLRPC.TL_messageMediaDocument && (getDocument() instanceof TLRPC.TL_documentEmpty || getDocument() == null) && getMedia(messageOwner).ttl_seconds != 0) { if (getMedia(messageOwner).ttl_seconds != 0 && !(messageOwner instanceof TLRPC.TL_message_secret)) { messageText = LocaleController.getString("AttachDestructingVideo", R.string.AttachDestructingVideo); } else { @@ -4098,7 +4104,7 @@ public void setType() { } } else if (hasExtendedMediaPreview()) { type = TYPE_EXTENDED_MEDIA_PREVIEW; - } else if (getMedia(messageOwner).ttl_seconds != 0 && (getMedia(messageOwner).photo instanceof TLRPC.TL_photoEmpty || getDocument() instanceof TLRPC.TL_documentEmpty)) { + } else if (getMedia(messageOwner).ttl_seconds != 0 && (getMedia(messageOwner).photo instanceof TLRPC.TL_photoEmpty || getDocument() instanceof TLRPC.TL_documentEmpty || getMedia(messageOwner) instanceof TLRPC.TL_messageMediaDocument && getDocument() == null || forceExpired)) { contentType = 1; type = TYPE_DATE; } else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaDice) { @@ -4889,6 +4895,28 @@ public void generateCaption() { caption = Emoji.replaceEmoji(text, Theme.chat_msgTextPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false); caption = replaceAnimatedEmoji(caption, entities, Theme.chat_msgTextPaint.getFontMetricsInt(), false); + int maxWidth = getMaxMessageTextWidth(); + final float lineSpacing = 1f; + final float lineAdd = 0; + Layout.Alignment align = Layout.Alignment.ALIGN_NORMAL; + StaticLayout captionLayout = null; + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + StaticLayout.Builder builder = + StaticLayout.Builder.obtain(caption, 0, caption.length(), Theme.chat_msgTextPaint, maxWidth) + .setLineSpacing(lineAdd, lineSpacing) + .setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY) + .setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE) + .setAlignment(align); + captionLayout = builder.build(); + } else { + captionLayout = new StaticLayout(caption, Theme.chat_msgTextPaint, maxWidth, align, lineSpacing, lineAdd, false); + } + } catch (Exception e) { + FileLog.e(e); + } + captionHeight = captionLayout == null ? 0 : captionLayout.getHeight(); + boolean hasEntities; if (messageOwner.send_state != MESSAGE_SEND_STATE_SENT) { hasEntities = false; @@ -6339,10 +6367,23 @@ public int getSecretTimeLeft() { return secondsLeft; } - public String getSecretTimeString() { + private CharSequence secretOnceSpan; + private CharSequence secretPlaySpan; + + public CharSequence getSecretTimeString() { if (!isSecretMedia()) { return null; } + if (messageOwner.ttl == 0x7FFFFFFF) { + if (secretOnceSpan == null) { + secretOnceSpan = new SpannableString("v"); + ColoredImageSpan span = new ColoredImageSpan(R.drawable.mini_viewonce); + span.setTranslateX(-AndroidUtilities.dp(3)); + span.setWidth(AndroidUtilities.dp(13)); + ((Spannable) secretOnceSpan).setSpan(span, 0, secretOnceSpan.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + return TextUtils.concat(secretOnceSpan, "1"); + } int secondsLeft = getSecretTimeLeft(); String str; if (secondsLeft < 60) { @@ -6350,7 +6391,14 @@ public String getSecretTimeString() { } else { str = secondsLeft / 60 + "m"; } - return str; + if (secretPlaySpan == null) { + secretPlaySpan = new SpannableString("p"); + ColoredImageSpan span = new ColoredImageSpan(R.drawable.play_mini_video); + span.setTranslateX(AndroidUtilities.dp(1)); + span.setWidth(AndroidUtilities.dp(13)); + ((Spannable) secretPlaySpan).setSpan(span, 0, secretPlaySpan.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + return TextUtils.concat(secretPlaySpan, str); } public String getDocumentName() { @@ -6907,6 +6955,11 @@ public int getApproximateHeight() { } photoHeight = h; } + + if (caption != null && !TextUtils.isEmpty(caption)) { + photoHeight += captionHeight; + } + return photoHeight + AndroidUtilities.dp(14); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java index dcaa89215a..c06a5c3ccd 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java @@ -72,6 +72,7 @@ import org.telegram.ui.LaunchActivity; import org.telegram.ui.PremiumPreviewFragment; import org.telegram.ui.ProfileActivity; +import org.telegram.ui.SecretMediaViewer; import org.telegram.ui.Stories.StoriesController; import org.telegram.ui.TopicsFragment; @@ -153,6 +154,7 @@ public class MessagesController extends BaseController implements NotificationCe public int stealthModePast; public int stealthModeCooldown; public StoriesController storiesController; + public UnconfirmedAuthController unconfirmedAuthController; private boolean hasArchivedChats; private boolean hasStories; public long storiesChangelogUserId = 777000; @@ -508,6 +510,7 @@ protected boolean useCache(Integer arguments) { public int ringtoneDurationMax; public int ringtoneSizeMax; public boolean storiesExportNopublicLink; + public int authorizationAutoconfirmPeriod; public int channelsLimitDefault; public int channelsLimitPremium; @@ -1478,6 +1481,7 @@ public MessagesController(int num) { storiesPosting = mainPreferences.getString("storiesPosting", "enabled"); storiesEntities = mainPreferences.getString("storiesEntities", "premium"); storiesExportNopublicLink = mainPreferences.getBoolean("storiesExportNopublicLink", false); + authorizationAutoconfirmPeriod = mainPreferences.getInt("authorization_autoconfirm_period", 604800); // BuildVars.GOOGLE_AUTH_CLIENT_ID = mainPreferences.getString("googleAuthClientId", BuildVars.GOOGLE_AUTH_CLIENT_ID); if (mainPreferences.contains("dcDomainName2")) { dcDomainName = mainPreferences.getString("dcDomainName2", "apv3.stel.com"); @@ -3424,6 +3428,7 @@ private void applyAppConfig(TLRPC.TL_jsonObject object) { changed = storiesChanged = true; } } + break; } case "stories_entities": { if (value.value instanceof TLRPC.TL_jsonString) { @@ -3434,6 +3439,7 @@ private void applyAppConfig(TLRPC.TL_jsonObject object) { changed = true; } } + break; } case "stories_export_nopublic_link": { if (value.value instanceof TLRPC.TL_jsonBool) { @@ -3444,6 +3450,7 @@ private void applyAppConfig(TLRPC.TL_jsonObject object) { changed = true; } } + break; } case "stories_venue_search_username": { if (value.value instanceof TLRPC.TL_jsonString) { @@ -3454,6 +3461,18 @@ private void applyAppConfig(TLRPC.TL_jsonObject object) { changed = true; } } + break; + } + case "authorization_autoconfirm_period": { + if (value.value instanceof TLRPC.TL_jsonNumber) { + TLRPC.TL_jsonNumber num = (TLRPC.TL_jsonNumber) value.value; + if (authorizationAutoconfirmPeriod != num.value) { + authorizationAutoconfirmPeriod = (int) num.value; + editor.putInt("authorizationAutoconfirmPeriod", authorizationAutoconfirmPeriod); + changed = true; + } + } + break; } } } @@ -4218,6 +4237,9 @@ public void cleanup() { if (storiesController != null) { storiesController.cleanup(); } + if (unconfirmedAuthController != null) { + unconfirmedAuthController.cleanup(); + } showFiltersTooltip = false; @@ -5740,8 +5762,22 @@ private boolean checkDeletingTask(boolean runnable) { } } if (taskMedia != null) { + final boolean checkViewer = SecretMediaViewer.hasInstance() && SecretMediaViewer.getInstance().isVisible(); + final MessageObject viewerObject = checkViewer ? SecretMediaViewer.getInstance().getCurrentMessageObject() : null; for (int a = 0, N = taskMedia.size(); a < N; a++) { - getMessagesStorage().emptyMessagesMedia(taskMedia.keyAt(a), taskMedia.valueAt(a)); + long dialogId = taskMedia.keyAt(a); + ArrayList mids = taskMedia.valueAt(a); + if (checkViewer && viewerObject != null && viewerObject.currentAccount == currentAccount && viewerObject.getDialogId() == dialogId && mids.contains(viewerObject.getId())) { + final int id = viewerObject.getId(); + mids.remove((Integer) id); + viewerObject.forceExpired = true; + final long taskId = createDeleteShowOnceTask(dialogId, id); + SecretMediaViewer.getInstance().setOnClose(() -> doDeleteShowOnceTask(taskId, dialogId, id)); + getNotificationCenter().postNotificationName(NotificationCenter.updateMessageMedia, viewerObject.messageOwner); + } + if (!mids.isEmpty()) { + getMessagesStorage().emptyMessagesMedia(dialogId, mids); + } } } Utilities.stageQueue.postRunnable(() -> { @@ -5953,7 +5989,7 @@ public void setUserAdminRole(long chatId, TLRPC.User user, TLRPC.TL_chatAdminRig AndroidUtilities.runOnUIThread(() -> { BaseFragment lastFragment = LaunchActivity.getLastFragment(); if (lastFragment != null && lastFragment.getParentActivity() != null) { - LimitReachedBottomSheet restricterdUsersBottomSheet = new LimitReachedBottomSheet(lastFragment, lastFragment.getParentActivity(), LimitReachedBottomSheet.TYPE_ADD_MEMBERS_RESTRICTED, currentAccount); + LimitReachedBottomSheet restricterdUsersBottomSheet = new LimitReachedBottomSheet(lastFragment, lastFragment.getParentActivity(), LimitReachedBottomSheet.TYPE_ADD_MEMBERS_RESTRICTED, currentAccount, null); ArrayList users = new ArrayList(); users.add(user); restricterdUsersBottomSheet.setRestrictedUsers(chat, users); @@ -10912,7 +10948,7 @@ public void markMessageContentAsRead(MessageObject messageObject) { } arrayList.add(messageObject.getId()); long dialogId = messageObject.getDialogId(); - getMessagesStorage().markMessagesContentAsRead(dialogId, arrayList, 0); + getMessagesStorage().markMessagesContentAsRead(dialogId, arrayList, 0, 0); getNotificationCenter().postNotificationName(NotificationCenter.messagesReadContent, dialogId, arrayList); if (messageObject.getId() < 0) { markMessageAsRead(messageObject.getDialogId(), messageObject.messageOwner.random_id, Integer.MIN_VALUE); @@ -10964,8 +11000,32 @@ public void markMentionMessageAsRead(int mid, long channelId, long did) { } } + public long createDeleteShowOnceTask(long dialogId, int mid) { + NativeByteBuffer data = null; + try { + data = new NativeByteBuffer(16); + data.writeInt32(102); + data.writeInt64(dialogId); + data.writeInt32(mid); + } catch (Exception e) { + FileLog.e(e); + } + return getMessagesStorage().createPendingTask(data); + } + + public void doDeleteShowOnceTask(long taskId, long dialogId, int mid) { + getMessagesStorage().removePendingTask(taskId); + ArrayList mids = new ArrayList<>(); + mids.add(mid); + getMessagesStorage().emptyMessagesMedia(dialogId, mids); + } + public void markMessageAsRead2(long dialogId, int mid, TLRPC.InputChannel inputChannel, int ttl, long taskId) { - if (mid == 0 || ttl <= 0) { + markMessageAsRead2(dialogId, mid, inputChannel, ttl, taskId, true); + } + + public void markMessageAsRead2(long dialogId, int mid, TLRPC.InputChannel inputChannel, int ttl, long taskId, boolean createDeleteTask) { + if (mid == 0 || ttl < 0) { return; } if (DialogObject.isChatDialog(dialogId) && inputChannel == null) { @@ -10979,7 +11039,7 @@ public void markMessageAsRead2(long dialogId, int mid, TLRPC.InputChannel inputC NativeByteBuffer data = null; try { data = new NativeByteBuffer(20 + (inputChannel != null ? inputChannel.getObjectSize() : 0)); - data.writeInt32(23); + data.writeInt32(createDeleteTask ? 23 : 101); data.writeInt64(dialogId); data.writeInt32(mid); data.writeInt32(ttl); @@ -10994,7 +11054,9 @@ public void markMessageAsRead2(long dialogId, int mid, TLRPC.InputChannel inputC newTaskId = taskId; } int time = getConnectionsManager().getCurrentTime(); - getMessagesStorage().createTaskForMid(dialogId, mid, time, time, ttl, false); + if (createDeleteTask) { + getMessagesStorage().createTaskForMid(dialogId, mid, time, time, ttl, false); + } if (inputChannel != null) { TLRPC.TL_channels_readMessageContents req = new TLRPC.TL_channels_readMessageContents(); req.channel = inputChannel; @@ -11733,7 +11795,7 @@ public void addUsersToChat(TLRPC.Chat currentChat, BaseFragment baseFragment, Ar BaseFragment lastFragment = LaunchActivity.getLastFragment(); 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); + LimitReachedBottomSheet restricterdUsersBottomSheet = new LimitReachedBottomSheet(lastFragment, lastFragment.getParentActivity(), LimitReachedBottomSheet.TYPE_ADD_MEMBERS_RESTRICTED, currentAccount, null); restricterdUsersBottomSheet.setRestrictedUsers(currentChat, userRestrictedPrivacy); restricterdUsersBottomSheet.show(); // } else { @@ -14467,6 +14529,7 @@ public boolean processUpdateArray(ArrayList updates, ArrayList> markContentAsReadMessages = null; SparseIntArray markAsReadEncrypted = null; LongSparseArray> deletedMessages = null; @@ -14721,6 +14784,7 @@ public boolean processUpdateArray(ArrayList updates, ArrayList(); } @@ -15118,6 +15182,11 @@ public boolean processUpdateArray(ArrayList updates, ArrayList(); } updatesOnMainThread.add(baseUpdate); + } else if (baseUpdate instanceof TLRPC.TL_updateNewAuthorization) { + if (updatesOnMainThread == null) { + updatesOnMainThread = new ArrayList<>(); + } + updatesOnMainThread.add(baseUpdate); } else if (baseUpdate instanceof TLRPC.TL_updateServiceNotification) { TLRPC.TL_updateServiceNotification update = (TLRPC.TL_updateServiceNotification) baseUpdate; if (update.popup && update.message != null && update.message.length() > 0) { @@ -16096,6 +16165,8 @@ public boolean processUpdateArray(ArrayList updates, ArrayList updates, ArrayList arrayList = markContentAsReadMessages.valueAt(a); - getMessagesStorage().markMessagesContentAsRead(key, arrayList, time); + getMessagesStorage().markMessagesContentAsRead(key, arrayList, currentTime2, markContentAsReadMessagesDate); } } if (deletedMessages != null) { @@ -18676,6 +18747,19 @@ public StoriesController getStoriesController() { return storiesController; } + public UnconfirmedAuthController getUnconfirmedAuthController() { + if (unconfirmedAuthController != null) { + return unconfirmedAuthController; + } + synchronized (lockObject) { + if (unconfirmedAuthController != null) { + return unconfirmedAuthController; + } + unconfirmedAuthController = new UnconfirmedAuthController(currentAccount); + } + return unconfirmedAuthController; + } + public boolean storiesEnabled() { if (NaConfig.INSTANCE.getDisableStories().Bool()) return false; switch (storiesPosting) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java index e4006d1ebe..f42a4d8964 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java @@ -15,6 +15,7 @@ import android.text.Spanned; import android.text.TextUtils; import android.text.style.ForegroundColorSpan; +import android.util.Log; import android.util.Pair; import android.util.SparseArray; import android.util.SparseIntArray; @@ -96,7 +97,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 = 129; + public final static int LAST_DB_VERSION = 133; private boolean databaseMigrationInProgress; public boolean showClearDatabaseAlert; private LongSparseIntArray dialogIsForum = new LongSparseIntArray(); @@ -607,6 +608,8 @@ public static void createTables(SQLiteDatabase database) throws SQLiteException database.executeFast("CREATE TABLE requested_holes(uid INTEGER, seq_out_start INTEGER, seq_out_end INTEGER, PRIMARY KEY (uid, seq_out_start, seq_out_end));").stepThis().dispose(); database.executeFast("CREATE TABLE sharing_locations(uid INTEGER PRIMARY KEY, mid INTEGER, date INTEGER, period INTEGER, message BLOB, proximity INTEGER);").stepThis().dispose(); + database.executeFast("CREATE INDEX IF NOT EXISTS stickers_featured_emoji_index ON stickers_featured(emoji);").stepThis().dispose(); + database.executeFast("CREATE TABLE shortcut_widget(id INTEGER, did INTEGER, ord INTEGER, PRIMARY KEY (id, did));").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS shortcut_widget_did ON shortcut_widget(did);").stepThis().dispose(); @@ -673,16 +676,17 @@ 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, custom_params BLOB, PRIMARY KEY (dialog_id, story_id));").stepThis().dispose(); + database.executeFast("CREATE TABLE stories (dialog_id INTEGER, story_id INTEGER, data BLOB, custom_params BLOB, 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 profile_stories (dialog_id INTEGER, story_id INTEGER, data BLOB, type INTEGER, PRIMARY KEY(dialog_id, story_id));").stepThis().dispose(); database.executeFast("CREATE TABLE story_drafts (id INTEGER PRIMARY KEY, date INTEGER, data BLOB, type INTEGER);").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("CREATE TABLE unconfirmed_auth (data BLOB);").stepThis().dispose(); + database.executeFast("PRAGMA user_version = " + MessagesStorage.LAST_DB_VERSION).stepThis().dispose(); } @@ -1034,6 +1038,7 @@ private void loadPendingTasks() { AndroidUtilities.runOnUIThread(() -> getMessagesController().markMessageAsRead2(-channelId, mid, inputChannel, ttl, taskId)); break; } + case 101: case 23: { TLRPC.InputChannel inputChannel; long dialogId = data.readInt64(false); @@ -1044,7 +1049,7 @@ private void loadPendingTasks() { } else { inputChannel = null; } - AndroidUtilities.runOnUIThread(() -> getMessagesController().markMessageAsRead2(dialogId, mid, inputChannel, ttl, taskId)); + AndroidUtilities.runOnUIThread(() -> getMessagesController().markMessageAsRead2(dialogId, mid, inputChannel, ttl, taskId, type == 23)); break; } case 12: @@ -1127,6 +1132,12 @@ private void loadPendingTasks() { AndroidUtilities.runOnUIThread(() -> getSecretChatHelper().declineSecretChat(chatId, revoke, taskId)); break; } + case 102: { + long dialogId = data.readInt64(false); + int mid = data.readInt32(false); + AndroidUtilities.runOnUIThread(() -> getMessagesController().doDeleteShowOnceTask(taskId, dialogId, mid)); + break; + } } data.reuse(); } @@ -1323,7 +1334,6 @@ public void clearLocalDatabase() { 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"); @@ -5046,7 +5056,7 @@ public void createTaskForMid(long dialogId, int messageId, int time, int readTim AndroidUtilities.runOnUIThread(() -> { if (!inner) { - markMessagesContentAsRead(dialogId, midsArray, 0); + markMessagesContentAsRead(dialogId, midsArray, 0, 0); } getNotificationCenter().postNotificationName(NotificationCenter.messagesReadContent, dialogId, midsArray); }); @@ -5078,6 +5088,59 @@ public void createTaskForMid(long dialogId, int messageId, int time, int readTim }); } + private void createTaskForSecretMedia(long dialogId, SparseArray> messages) { + SQLiteCursor cursor = null; + SQLitePreparedStatement state = null; + try { + int minDate = Integer.MAX_VALUE; + +// if (random_ids != null) { +// AndroidUtilities.runOnUIThread(() -> { +// markMessagesContentAsRead(dialogId, mids, 0, 0); +// getNotificationCenter().postNotificationName(NotificationCenter.messagesReadContent, dialogId, mids); +// }); +// } + + ArrayList mids = new ArrayList<>(); + if (messages.size() != 0) { + database.beginTransaction(); + state = database.executeFast("REPLACE INTO enc_tasks_v4 VALUES(?, ?, ?, ?)"); + for (int a = 0; a < messages.size(); a++) { + int key = messages.keyAt(a); + ArrayList arr = messages.get(key); + for (int b = 0; b < arr.size(); b++) { + int date = arr.get(b); + state.requery(); + state.bindInteger(1, date); + state.bindLong(2, dialogId); + state.bindInteger(3, key); + state.bindInteger(4, 1); + minDate = Math.min(minDate, date); + state.step(); + mids.add(arr.get(b)); + } + } + state.dispose(); + state = null; + database.commitTransaction(); + database.executeFast(String.format(Locale.US, "UPDATE messages_v2 SET ttl = 0 WHERE uid = %d AND mid IN(%s)", dialogId, TextUtils.join(", ", mids))).stepThis().dispose(); + getMessagesController().didAddedNewTask(minDate, dialogId, messages); + } + } catch (Exception e) { + checkSQLException(e); + } finally { + if (database != null) { + database.commitTransaction(); + } + if (state != null) { + state.dispose(); + } + if (cursor != null) { + cursor.dispose(); + } + } + } + public void createTaskForSecretChat(int chatId, int time, int readTime, int isOut, ArrayList random_ids) { storageQueue.postRunnable(() -> { SQLiteCursor cursor = null; @@ -5121,7 +5184,7 @@ public void createTaskForSecretChat(int chatId, int time, int readTime, int isOu if (random_ids != null) { AndroidUtilities.runOnUIThread(() -> { - markMessagesContentAsRead(dialogId, midsArray, 0); + markMessagesContentAsRead(dialogId, midsArray, 0, 0); getNotificationCenter().postNotificationName(NotificationCenter.messagesReadContent, dialogId, midsArray); }); } @@ -9794,10 +9857,10 @@ public void getDownloadQueue(int type) { data.reuse(); if (messageMedia.document != null) { downloadObject.object = messageMedia.document; - downloadObject.secret = MessageObject.isVideoDocument(messageMedia.document) && messageMedia.ttl_seconds > 0 && messageMedia.ttl_seconds <= 60; + downloadObject.secret = MessageObject.isVideoDocument(messageMedia.document) && (messageMedia.ttl_seconds > 0 && messageMedia.ttl_seconds <= 60 || messageMedia.ttl_seconds == 0x7FFFFFFF); } else if (messageMedia.photo != null) { downloadObject.object = messageMedia.photo; - downloadObject.secret = messageMedia.ttl_seconds > 0 && messageMedia.ttl_seconds <= 60; + downloadObject.secret = messageMedia.ttl_seconds > 0 && messageMedia.ttl_seconds <= 60 || messageMedia.ttl_seconds == 0x7FFFFFFF; } downloadObject.forceCache = (messageMedia.flags & 0x80000000) != 0; } @@ -12086,7 +12149,7 @@ private void markMessagesContentAsReadInternal(long dialogId, ArrayList } } - public void markMessagesContentAsRead(long dialogId, ArrayList mids, int date) { + public void markMessagesContentAsRead(long dialogId, ArrayList mids, int currentDate, int readDate) { if (isEmpty(mids)) { return; } @@ -12095,21 +12158,39 @@ public void markMessagesContentAsRead(long dialogId, ArrayList mids, in if (dialogId == 0) { SQLiteCursor cursor = null; try { - LongSparseArray> sparseArray = new LongSparseArray<>(); - cursor = database.queryFinalized(String.format(Locale.US, "SELECT uid, mid FROM messages_v2 WHERE mid IN (%s) AND is_channel = 0", TextUtils.join(",", mids))); + LongSparseArray> toDelete = new LongSparseArray<>(); + LongSparseArray>> toTask = new LongSparseArray<>(); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT uid, mid, ttl FROM messages_v2 WHERE mid IN (%s) AND is_channel = 0", TextUtils.join(",", mids))); while (cursor.next()) { long did = cursor.longValue(0); - ArrayList arrayList = sparseArray.get(did); - if (arrayList == null) { - arrayList = new ArrayList<>(); - sparseArray.put(did, arrayList); + int mid = cursor.intValue(1); + int ttl = cursor.intValue(2); + if (ttl <= 0 || ttl == 0x7FFFFFFF || readDate == 0 || readDate + ttl < currentDate) { + ArrayList arrayList = toDelete.get(did); + if (arrayList == null) { + toDelete.put(did, arrayList = new ArrayList<>()); + } + arrayList.add(mid); + } else { + int date = readDate + ttl; + SparseArray> array = toTask.get(did); + if (array == null) { + toTask.put(did, array = new SparseArray<>()); + } + ArrayList msgs = array.get(date); + if (msgs == null) { + array.put(date, msgs = new ArrayList<>()); + } + msgs.add(mid); } - arrayList.add(cursor.intValue(1)); } cursor.dispose(); cursor = null; - for (int a = 0, N = sparseArray.size(); a < N; a++) { - markMessagesContentAsReadInternal(sparseArray.keyAt(a), sparseArray.valueAt(a), date); + for (int a = 0, N = toDelete.size(); a < N; a++) { + markMessagesContentAsReadInternal(toDelete.keyAt(a), toDelete.valueAt(a), currentDate); + } + for (int a = 0, N = toTask.size(); a < N; a++) { + createTaskForSecretMedia(toTask.keyAt(a), toTask.valueAt(a)); } } catch (Exception e) { checkSQLException(e); @@ -12119,7 +12200,7 @@ public void markMessagesContentAsRead(long dialogId, ArrayList mids, in } } } else { - markMessagesContentAsReadInternal(dialogId, mids, date); + markMessagesContentAsReadInternal(dialogId, mids, currentDate); } }); } @@ -15372,6 +15453,17 @@ public ArrayList getUsers(ArrayList uids) { return users; } + public ArrayList getChats(ArrayList dids) { + ArrayList chats = new ArrayList<>(); + try { + getChatsInternal(TextUtils.join(",", dids), chats); + } catch (Exception e) { + chats.clear(); + checkSQLException(e); + } + return chats; + } + public TLRPC.Chat getChat(long chatId) { TLRPC.Chat chat = null; try { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NativeLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/NativeLoader.java index 207fa7f434..a250fb02b5 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NativeLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NativeLoader.java @@ -19,7 +19,7 @@ public class NativeLoader { - private final static int LIB_VERSION = 45; + private final static int LIB_VERSION = 46; private final static String LIB_NAME = "tmessages." + LIB_VERSION; private final static String LIB_SO_NAME = "lib" + LIB_NAME + ".so"; private final static String LOCALE_LIB_SO_NAME = "lib" + LIB_NAME + "loc.so"; @@ -68,7 +68,7 @@ public static synchronized void initNativeLibs(Context context) { nativeLoaded = true; } catch (Error e) { FileLog.e(e); - log.append("185: ").append(e).append("\n"); + log.append("184: ").append(e).append("\n"); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java index 2644078426..f2c2c521f2 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java @@ -216,6 +216,8 @@ public class NotificationCenter { public static final int storiesEnabledUpdate = totalEvents++; public static final int storiesBlocklistUpdate = totalEvents++; public static final int storiesLimitUpdate = totalEvents++; + public static final int storiesSendAsUpdate = totalEvents++; + public static final int unconfirmedAuthUpdate = totalEvents++; //global public static final int pushMessagesUpdated = totalEvents++; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java index c49bc32c80..7a9e8798d0 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java @@ -994,8 +994,6 @@ public void processNewMessages(ArrayList messageObjects, boolean 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; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java b/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java index 1d29008a14..9d6ce82e54 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java @@ -7833,7 +7833,7 @@ public static void prepareSendingMedia(AccountInstance accountInstance, ArrayLis accountInstance.getUserConfig().saveConfig(false); TLRPC.TL_documentAttributeVideo attributeVideo; if (isEncrypted) { - attributeVideo = new TLRPC.TL_documentAttributeVideo(); + attributeVideo = new TLRPC.TL_documentAttributeVideo_layer159(); } else { attributeVideo = new TLRPC.TL_documentAttributeVideo(); attributeVideo.supports_streaming = true; @@ -8506,7 +8506,7 @@ public static void prepareSendingVideo(AccountInstance accountInstance, String v if (encryptedChat == null) { return; } - attributeVideo = new TLRPC.TL_documentAttributeVideo(); + attributeVideo = new TLRPC.TL_documentAttributeVideo_layer159(); } else { attributeVideo = new TLRPC.TL_documentAttributeVideo(); attributeVideo.supports_streaming = true; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java b/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java index 7920657908..3c82078eba 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java @@ -183,6 +183,14 @@ public static void toggleSurfaceInStories() { .apply(); } + public static void togglePhotoViewerBlur() { + photoViewerBlur = !photoViewerBlur; + ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE) + .edit() + .putBoolean("photoViewerBlur", photoViewerBlur) + .apply(); + } + private static String goodHevcEncoder; private static HashSet hevcEncoderWhitelist = new HashSet<>(); static { @@ -276,6 +284,7 @@ private static boolean isWhitelisted(MediaCodecInfo codecInfo) { public static boolean updateStickersOrderOnSend = true; public static boolean bigCameraForRound; public static boolean useSurfaceInStories; + public static boolean photoViewerBlur = true; public static int stealthModeSendMessageConfirm = 2; private static int lastLocalId = -210000; @@ -1516,6 +1525,9 @@ public static void loadConfig() { dayNightWallpaperSwitchHint = preferences.getInt("dayNightWallpaperSwitchHint", 0); bigCameraForRound = preferences.getBoolean("bigCameraForRound", false); useSurfaceInStories = preferences.getBoolean("useSurfaceInStories", Build.VERSION.SDK_INT >= 30); + photoViewerBlur = preferences.getBoolean("photoViewerBlur", true); + + loadDebugConfig(preferences); preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); showNotificationsForAllAccounts = preferences.getBoolean("AllAccounts", true); @@ -2767,4 +2779,20 @@ public static int getLegacyDevicePerformanceClass() { } return legacyDevicePerformanceClass; } + + + //DEBUG + public static boolean drawActionBarShadow = true; + + private static void loadDebugConfig(SharedPreferences preferences) { + drawActionBarShadow = preferences.getBoolean("drawActionBarShadow", true); + } + + public static void saveDebugConfig() { + SharedPreferences pref = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); + pref.edit().putBoolean("drawActionBarShadow", drawActionBarShadow); + } + + + } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/UnconfirmedAuthController.java b/TMessagesProj/src/main/java/org/telegram/messenger/UnconfirmedAuthController.java new file mode 100644 index 0000000000..e3451f1012 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/UnconfirmedAuthController.java @@ -0,0 +1,332 @@ +package org.telegram.messenger; + +import android.content.Context; + +import org.telegram.SQLite.SQLiteCursor; +import org.telegram.SQLite.SQLiteDatabase; +import org.telegram.SQLite.SQLitePreparedStatement; +import org.telegram.tgnet.AbstractSerializedData; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.NativeByteBuffer; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.BottomSheet; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Locale; + +public class UnconfirmedAuthController { + + private final int currentAccount; + + public UnconfirmedAuthController(int currentAccount) { + this.currentAccount = currentAccount; + readCache(); + } + + public final ArrayList auths = new ArrayList<>(); + + private boolean fetchedCache, fetchingCache; + private boolean saveAfterFetch; + + public void readCache() { + if (fetchedCache || fetchingCache) { + return; + } + fetchingCache = true; + MessagesStorage.getInstance(currentAccount).getStorageQueue().postRunnable(() -> { + final HashSet hashes = new HashSet<>(); + final ArrayList result = new ArrayList<>(); + SQLiteDatabase database = MessagesStorage.getInstance(currentAccount).getDatabase(); + SQLiteCursor cursor = null; + try { + cursor = database.queryFinalized(String.format(Locale.US, "SELECT data FROM unconfirmed_auth")); + while (cursor.next()) { + NativeByteBuffer data = cursor.byteBufferValue(0); + if (data != null) { + try { + UnconfirmedAuth auth = new UnconfirmedAuth(data); + result.add(auth); + hashes.add(auth.hash); + } catch (Exception e) { + FileLog.e(e); + } + } + } + } catch (Exception e) { + FileLog.e(e); + } finally { + if (cursor != null) { + cursor.dispose(); + cursor = null; + } + } + + AndroidUtilities.runOnUIThread(() -> { + boolean wasEmpty = auths.isEmpty(); + for (int i = 0; i < auths.size(); ++i) { + UnconfirmedAuth existingAuth = auths.get(i); + if (existingAuth == null || existingAuth.expired() || hashes.contains(existingAuth.hash)) { + auths.remove(i); + i--; + } + } + auths.addAll(result); + boolean isEmpty = auths.isEmpty(); + + fetchedCache = true; + fetchingCache = false; + + if (wasEmpty != isEmpty) { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.unconfirmedAuthUpdate); + } + scheduleAuthExpireCheck(); + + if (saveAfterFetch) { + saveAfterFetch = false; + saveCache(); + } + }); + }); + } + + private void scheduleAuthExpireCheck() { + AndroidUtilities.cancelRunOnUIThread(checkExpiration); + if (auths.isEmpty()) { + return; + } + + long minTime = Long.MAX_VALUE; + for (UnconfirmedAuth auth : auths) { + minTime = Math.min(minTime, auth.expiresAfter()); + } + if (minTime == Long.MAX_VALUE) { + return; + } + AndroidUtilities.runOnUIThread(checkExpiration, Math.max(0, minTime * 1000L)); + } + + private final Runnable checkExpiration = () -> { + for (int i = 0; i < auths.size(); ++i) { + UnconfirmedAuth auth = auths.get(i); + if (auth.expired()) { + auths.remove(i); + i--; + } + } + saveCache(); + }; + + private boolean debug = false; + public void putDebug() { + debug = true; + TLRPC.TL_updateNewAuthorization update = new TLRPC.TL_updateNewAuthorization(); + update.unconfirmed = true; + update.device = "device"; + update.location = "location"; + update.hash = 123; + processUpdate(update); + } + + public void processUpdate(TLRPC.TL_updateNewAuthorization update) { + for (int i = 0; i < auths.size(); ++i) { + UnconfirmedAuth auth = auths.get(i); + if (auth != null && auth.hash == update.hash) { + auths.remove(i); + i--; + } + } + if (update.unconfirmed) { + auths.add(new UnconfirmedAuth(update)); + } + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.unconfirmedAuthUpdate); + scheduleAuthExpireCheck(); + saveCache(); + } + + private boolean savingCache; + public void saveCache() { + if (savingCache) { + return; + } + if (fetchingCache) { + saveAfterFetch = true; + return; + } + savingCache = true; + MessagesStorage.getInstance(currentAccount).getStorageQueue().postRunnable(() -> { + SQLiteDatabase database = MessagesStorage.getInstance(currentAccount).getDatabase(); + SQLitePreparedStatement state = null; + try { + database.executeFast("DELETE FROM unconfirmed_auth WHERE 1").stepThis().dispose(); + state = database.executeFast("REPLACE INTO unconfirmed_auth VALUES(?)"); + for (UnconfirmedAuth auth : auths) { + state.requery(); + NativeByteBuffer buffer = new NativeByteBuffer(auth.getObjectSize()); + auth.serializeToStream(buffer); + state.bindByteBuffer(1, buffer); + state.step(); + } + } catch (Exception e) { + FileLog.e(e); + } finally { + if (state != null) { + state.dispose(); + state = null; + } + } + AndroidUtilities.runOnUIThread(() -> { + savingCache = false; + }); + }); + } + + public void cleanup() { + auths.clear(); + saveCache(); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.unconfirmedAuthUpdate); + scheduleAuthExpireCheck(); + } + + private void updateList(boolean confirm, ArrayList _list, Utilities.Callback> whenDone) { + final ArrayList list = new ArrayList<>(_list); + final boolean[] results = new boolean[list.size()]; + final Utilities.Callback[] callbacks = new Utilities.Callback[list.size()]; + for (int i = 0; i < list.size(); ++i) { + final int a = i; + final UnconfirmedAuth auth = list.get(a); + callbacks[a] = finish -> { + Utilities.Callback next = success -> { results[a] = success; finish.run(); }; + if (confirm) { + auth.confirm(next); + } else { + auth.deny(next); + } + }; + } + Utilities.raceCallbacks( + () -> { + final HashSet hashes = new HashSet<>(); + final ArrayList success = new ArrayList<>(); + for (int i = 0; i < results.length; ++i) { + if (results[i]) { + UnconfirmedAuth auth = list.get(i); + success.add(auth); + hashes.add(auth.hash); + } + } + if (!confirm) { + for (int i = 0; i < auths.size(); ++i) { + if (hashes.contains(auths.get(i).hash)) { + auths.remove(i); + i--; + } + } + if (!hashes.isEmpty()) { + saveCache(); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.unconfirmedAuthUpdate); + scheduleAuthExpireCheck(); + } + } + whenDone.run(success); + }, + callbacks + ); + if (confirm) { + final HashSet hashes = new HashSet<>(); + for (int i = 0; i < list.size(); ++i) { + hashes.add(list.get(i).hash); + } + for (int i = 0; i < auths.size(); ++i) { + if (hashes.contains(auths.get(i).hash)) { + auths.remove(i); + i--; + } + } + if (!hashes.isEmpty()) { + saveCache(); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.unconfirmedAuthUpdate); + scheduleAuthExpireCheck(); + } + } + } + + public void confirm(ArrayList list, Utilities.Callback> whenDone) { + updateList(true, list, whenDone); + } + + public void deny(ArrayList list, Utilities.Callback> whenDone) { + updateList(false, list, whenDone); + } + + public class UnconfirmedAuth extends TLObject { + + public long hash; + public int date; + public String device; + public String location; + + public UnconfirmedAuth(AbstractSerializedData stream) { + int magic = stream.readInt32(true); + if (magic != 0x7ab6618c) { + throw new RuntimeException("UnconfirmedAuth can't parse magic " + Integer.toHexString(magic)); + } + hash = stream.readInt64(true); + date = stream.readInt32(true); + device = stream.readString(true); + location = stream.readString(true); + } + + public UnconfirmedAuth(TLRPC.TL_updateNewAuthorization update) { + hash = update.hash; + date = update.date; + device = update.device; + location = update.location; + } + + @Override + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(0x7ab6618c); + stream.writeInt64(hash); + stream.writeInt32(date); + stream.writeString(device); + stream.writeString(location); + } + + public long expiresAfter() { + return ConnectionsManager.getInstance(currentAccount).getCurrentTime() + MessagesController.getInstance(currentAccount).authorizationAutoconfirmPeriod - date; + } + + public boolean expired() { + return expiresAfter() <= 0; + } + + public void confirm(Utilities.Callback whenDone) { + TLRPC.TL_account_changeAuthorizationSettings req = new TLRPC.TL_account_changeAuthorizationSettings(); + req.hash = hash; + req.confirmed = true; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> { + AndroidUtilities.runOnUIThread(() -> { + if (whenDone != null) { + whenDone.run(res instanceof TLRPC.TL_boolTrue && err == null || debug); + debug = false; + } + }); + }); + } + + public void deny(Utilities.Callback whenDone) { + TLRPC.TL_account_resetAuthorization req = new TLRPC.TL_account_resetAuthorization(); + req.hash = hash; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> { + AndroidUtilities.runOnUIThread(() -> { + if (whenDone != null) { + whenDone.run(res instanceof TLRPC.TL_boolTrue && err == null || debug); + debug = false; + } + }); + }); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java index d7a3efa9f1..5da83a9faa 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java @@ -22,7 +22,9 @@ import java.nio.ByteBuffer; import java.security.MessageDigest; import java.security.SecureRandom; +import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -517,10 +519,22 @@ public static interface Callback { public void run(T arg); } + public static interface CallbackVoidReturn { + public ReturnType run(); + } + public static interface CallbackReturn { public ReturnType run(Arg arg); } + public static interface Callback2Return { + public ReturnType run(T1 arg, T2 arg2); + } + + public static interface Callback3Return { + public ReturnType run(T1 arg, T2 arg2, T3 arg3); + } + public static interface Callback2 { public void run(T arg, T2 arg2); } @@ -532,6 +546,9 @@ public static interface Callback3 { public static interface Callback4 { public void run(T arg, T2 arg2, T3 arg3, T4 arg4); } + public static interface Callback5 { + public void run(T arg, T2 arg2, T3 arg3, T4 arg4, T5 arg5); + } public static Value getOrDefault(HashMap map, Key key, Value defaultValue) { Value v = map.get(key); @@ -577,4 +594,9 @@ public static DispatchQueue getOrCreatePlayerQueue() { } return videoPlayerQueue; } + + public static boolean isNullOrEmpty(final Collection list) { + return list == null || list.isEmpty(); + } + } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/VideoEditedInfo.java b/TMessagesProj/src/main/java/org/telegram/messenger/VideoEditedInfo.java index 0eb03d41f0..a329c51220 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/VideoEditedInfo.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/VideoEditedInfo.java @@ -13,6 +13,7 @@ import android.text.TextUtils; import android.view.View; +import org.telegram.messenger.video.MediaCodecVideoConvertor; import org.telegram.tgnet.AbstractSerializedData; import org.telegram.tgnet.SerializedData; import org.telegram.tgnet.TLRPC; @@ -20,6 +21,8 @@ import org.telegram.ui.Components.Paint.PaintTypeface; import org.telegram.ui.Components.PhotoFilterView; import org.telegram.ui.Components.Point; +import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; +import org.telegram.ui.Components.Reactions.ReactionsUtils; import org.telegram.ui.Stories.recorder.StoryEntry; import java.util.ArrayList; @@ -52,7 +55,7 @@ public class VideoEditedInfo { public byte[] key; public byte[] iv; public MediaController.SavedFilterState filterState; - public String paintPath; + public String paintPath, blurPath; public ArrayList mediaEntities; public MediaController.CropState cropState; public boolean isPhoto; @@ -72,6 +75,8 @@ public class VideoEditedInfo { public boolean tryUseHevc = false; public boolean fromCamera; + public ArrayList mixedSoundInfos = new ArrayList<>(); + public static class EmojiEntity extends TLRPC.TL_messageEntityCustomEmoji { public String documentAbsolutePath; @@ -108,6 +113,7 @@ public static class MediaEntity { public static final int TYPE_TEXT = 1; public static final int TYPE_PHOTO = 2; public static final int TYPE_LOCATION = 3; + public static final byte TYPE_REACTION = 4; public byte type; public byte subType; @@ -117,7 +123,7 @@ public static class MediaEntity { public float width; public float height; public float additionalWidth, additionalHeight; - public String text; + public String text = ""; public ArrayList entities = new ArrayList<>(); public int color; public int fontSize; @@ -153,6 +159,7 @@ public static class MediaEntity { public float density; public int W, H; + public ReactionsLayoutInBubble.VisibleReaction visibleReaction; public MediaEntity() { @@ -207,6 +214,9 @@ public MediaEntity(AbstractSerializedData data, boolean full) { } } } + if (type == TYPE_REACTION) { + mediaArea = TLRPC.MediaArea.TLdeserialize(data, data.readInt32(false), false); + } } public void serializeTo(AbstractSerializedData data, boolean full) { @@ -260,6 +270,9 @@ public void serializeTo(AbstractSerializedData data, boolean full) { data.writeInt32(TLRPC.TL_null.constructor); } } + if (type == TYPE_REACTION) { + mediaArea.serializeToStream(data); + } } public MediaEntity copy() { @@ -290,7 +303,7 @@ public MediaEntity copy() { public String getString() { String filters; - if (avatarStartTime != -1 || filterState != null || paintPath != null || mediaEntities != null && !mediaEntities.isEmpty() || cropState != null) { + if (avatarStartTime != -1 || filterState != null || paintPath != null || blurPath != null || mediaEntities != null && !mediaEntities.isEmpty() || cropState != null) { int len = 10; if (filterState != null) { len += 160; @@ -302,8 +315,15 @@ public String getString() { } else { paintPathBytes = null; } + byte[] blurPathBytes; + if (blurPath != null) { + blurPathBytes = blurPath.getBytes(); + len += blurPathBytes.length; + } else { + blurPathBytes = null; + } SerializedData serializedData = new SerializedData(len); - serializedData.writeInt32(7); + serializedData.writeInt32(8); serializedData.writeInt64(avatarStartTime); serializedData.writeInt32(originalBitrate); if (filterState != null) { @@ -395,6 +415,12 @@ public String getString() { } serializedData.writeBool(isStory); serializedData.writeBool(fromCamera); + if (blurPathBytes != null) { + serializedData.writeByte(1); + serializedData.writeByteArray(blurPathBytes); + } else { + serializedData.writeByte(0); + } filters = Utilities.bytesToHex(serializedData.toByteArray()); serializedData.cleanup(); } else { @@ -519,6 +545,13 @@ public boolean parseString(String string) { isStory = serializedData.readBool(false); fromCamera = serializedData.readBool(false); } + if (version >= 8) { + has = serializedData.readByte(false); + if (has != 0) { + byte[] bytes = serializedData.readByteArray(false); + blurPath = new String(bytes); + } + } serializedData.cleanup(); } } else { @@ -545,9 +578,9 @@ public boolean needConvert() { 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 !mixedSoundInfos.isEmpty() || mediaEntities != null || paintPath != null || blurPath != 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; + return !mixedSoundInfos.isEmpty() || mediaEntities != null || paintPath != null || blurPath != null || filterState != null || cropState != null || !roundVideo || startTime > 0 || endTime != -1 && endTime != estimatedDuration; } public boolean canAutoPlaySourceVideo() { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/AudioBufferConverter.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/AudioBufferConverter.java new file mode 100644 index 0000000000..0af9045f56 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/AudioBufferConverter.java @@ -0,0 +1,88 @@ +package org.telegram.messenger.video; + +import androidx.annotation.NonNull; + +import org.telegram.messenger.video.remix.AudioRemixer; +import org.telegram.messenger.video.remix.DefaultAudioRemixer; +import org.telegram.messenger.video.resample.AudioResampler; +import org.telegram.messenger.video.resample.DefaultAudioResampler; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.ShortBuffer; + +public class AudioBufferConverter { + private static final String TAG = AudioBufferConverter.class.getSimpleName(); + private static final int BYTES_PER_SHORT = 2; + + private final AudioRemixer mRemixer; + private final AudioResampler mResampler; + + public AudioBufferConverter() { + // Create remixer and resampler. + mRemixer = new DefaultAudioRemixer(); + mResampler = new DefaultAudioResampler(); + } + + public int calculateRequiredOutputSize(int inputSize, int inputSampleRate, int inputChannelCount, + int outputSampleRate, int outputChannelCount){ + checkChannels(inputChannelCount, outputChannelCount); + + int requiredOutputSize = inputSize; + + // Ask remixer how much space it needs for the given input + requiredOutputSize = mRemixer.getRemixedSize(requiredOutputSize, inputChannelCount, outputChannelCount); + + // After remixing we'll resample. + // Resampling will change the input size based on the sample rate ratio. + requiredOutputSize = (int) Math.ceil((double) requiredOutputSize * + outputSampleRate / (double)inputSampleRate); + return requiredOutputSize; + } + + public ShortBuffer convert(@NonNull ShortBuffer inputBuffer, int inputSampleRate, int inputChannelCount, + int outputSampleRate, int outputChannelCount) { + + checkChannels(inputChannelCount, outputChannelCount); + + final int inputSize = inputBuffer.remaining(); + + // Do the remixing. + int remixSize = mRemixer.getRemixedSize(inputSize, inputChannelCount, outputChannelCount); + ShortBuffer remixedBuffer = createBuffer(remixSize); + mRemixer.remix(inputBuffer, inputChannelCount, remixedBuffer, outputChannelCount); + remixedBuffer.rewind(); + + // Do the resampling. + int resampleSize = (int) Math.ceil((double) remixSize * outputSampleRate / (double)inputSampleRate); + // We add some extra values to avoid BufferOverflowException. + // Problem may occur for calculation. + // To be safe we add 10 but 1 is enough may be. Not sure. + resampleSize += 10; + + ShortBuffer outputBuffer = createBuffer(resampleSize); + mResampler.resample(remixedBuffer, inputSampleRate, outputBuffer, outputSampleRate, inputChannelCount); + outputBuffer.limit(outputBuffer.position()); + outputBuffer.rewind(); + return outputBuffer; + } + + private void checkChannels(int inputChannelCount, int outputChannelCount){ + // Check channel count. + if (inputChannelCount != 1 && inputChannelCount != 2) { + throw new UnsupportedOperationException("Input channel count (" + inputChannelCount + ") not supported."); + } + if (outputChannelCount != 1 && outputChannelCount != 2) { + throw new UnsupportedOperationException("Output channel count (" + outputChannelCount + ") not supported."); + } + } + + private ShortBuffer createBuffer(int capacity) { + ShortBuffer buffer = ByteBuffer.allocateDirect(capacity * BYTES_PER_SHORT) + .order(ByteOrder.nativeOrder()) + .asShortBuffer(); + buffer.clear(); + buffer.limit(capacity); + return buffer; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/AudioConversions.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/AudioConversions.java new file mode 100644 index 0000000000..64a891d9fc --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/AudioConversions.java @@ -0,0 +1,34 @@ +package org.telegram.messenger.video; + +public class AudioConversions { + + private static final int BYTES_PER_SAMPLE_PER_CHANNEL = 2; // Assuming 16bit audio, so 2 + private static final long MICROSECONDS_PER_SECOND = 1000000L; + private static final int BYTES_PER_SHORT = 2; + + @SuppressWarnings("WeakerAccess") + public static long bytesToUs( + int bytes /* bytes */, + int sampleRate /* samples/sec */, + int channels /* channel */ + ) { + int byteRatePerChannel = sampleRate * BYTES_PER_SAMPLE_PER_CHANNEL; // bytes/sec/channel + int byteRate = byteRatePerChannel * channels; // bytes/sec + return MICROSECONDS_PER_SECOND * bytes / byteRate; // usec + } + + @SuppressWarnings("WeakerAccess") + public static int usToBytes(long us, int sampleRate, int channels) { + int byteRatePerChannel = sampleRate * BYTES_PER_SAMPLE_PER_CHANNEL; + int byteRate = byteRatePerChannel * channels; + return (int) Math.ceil((double) us * byteRate / MICROSECONDS_PER_SECOND); + } + + public static long shortsToUs(int shorts, int sampleRate, int channels) { + return bytesToUs(shorts * BYTES_PER_SHORT, sampleRate, channels); + } + + public static int usToShorts(long us, int sampleRate, int channels) { + return usToBytes(us, sampleRate, channels) / BYTES_PER_SHORT; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/AudioDecoder.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/AudioDecoder.java new file mode 100644 index 0000000000..6247c6fea8 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/AudioDecoder.java @@ -0,0 +1,284 @@ +package org.telegram.messenger.video; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.media.MediaCodec; +import android.media.MediaDataSource; +import android.media.MediaExtractor; +import android.media.MediaFormat; +import android.net.Uri; +import android.os.Build; + +import org.telegram.messenger.FileLog; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Map; + +public class AudioDecoder { + private static final int TIMEOUT_USEC = 0000; + + private final MediaExtractor extractor; + private MediaCodec decoder; + private int trackIndex; + + private long startTimeUs; + private long endTimeUs; + private boolean loopingEnabled; + + private boolean allInputExtracted; + private boolean decodingDone; + private int audioIndex = -1; + + public AudioDecoder(String sourcePath) throws IOException { + extractor = new MediaExtractor(); + extractor.setDataSource(sourcePath); + init(); + } + + public AudioDecoder(String sourcePath, int audioIndex) throws IOException { + extractor = new MediaExtractor(); + extractor.setDataSource(sourcePath); + this.audioIndex = audioIndex; + init(); + } + + private void init() throws IOException { + selectTrack(); + + MediaFormat inputFormat = extractor.getTrackFormat(trackIndex); + decoder = MediaCodec.createDecoderByType(inputFormat.getString(MediaFormat.KEY_MIME)); + decoder.configure(inputFormat, null, null, 0); + + startTimeUs = 0; + endTimeUs = getDurationUs(); + } + + private void selectTrack() { + trackIndex = audioIndex; + if (trackIndex == -1) { + int numTracks = extractor.getTrackCount(); + for (int i = 0; i < numTracks; i++) { + MediaFormat format = extractor.getTrackFormat(i); + String mime = format.getString(MediaFormat.KEY_MIME); + if (mime != null && mime.startsWith("audio/")) { + trackIndex = i; + break; + } + } + } + if (trackIndex < 0) { + throw new RuntimeException("No audio track found in source"); + } + extractor.selectTrack(trackIndex); + } + + public MediaFormat getMediaFormat() { + try { + return extractor.getTrackFormat(trackIndex); + } catch (Exception e) { + FileLog.e(e); + } + return null; + } + + public long getDurationUs() { + try { + return getMediaFormat().getLong(MediaFormat.KEY_DURATION); + } catch (Exception e) { + FileLog.e(e); + } + return -1; + } + + public int getSampleRate() { + try { + return getMediaFormat().getInteger(MediaFormat.KEY_SAMPLE_RATE); + } catch (Exception e) { + FileLog.e(e); + } + return -1; + } + + public int getBitrateRate() { + try { + return getMediaFormat().getInteger(MediaFormat.KEY_BIT_RATE); + } catch (Exception e) { + } + return -1; + } + + public int getChannelCount() { + try { + return getMediaFormat().getInteger(MediaFormat.KEY_CHANNEL_COUNT); + } catch (Exception e) { + FileLog.e(e); + } + return -1; + } + + public long getStartTimeUs() { + return startTimeUs; + } + + public long getEndTimeUs() { + return endTimeUs; + } + + public boolean isLoopingEnabled() { + return loopingEnabled; + } + + public boolean isDecodingDone() { + return decodingDone; + } + + public void setStartTimeUs(long startTimeUs) { + this.startTimeUs = startTimeUs; + + long durationUs = getDurationUs(); + if (startTimeUs < 0) this.startTimeUs = 0; + else if (startTimeUs > durationUs) this.startTimeUs = durationUs; + } + + public void setEndTimeUs(long endTimeUs) { + this.endTimeUs = endTimeUs; + + long durationUs = getDurationUs(); + if (endTimeUs < 0) { + this.endTimeUs = 0; + } else { + if (endTimeUs > durationUs) this.endTimeUs = durationUs; + } + } + + public void setLoopingEnabled(boolean loopingEnabled) { + this.loopingEnabled = loopingEnabled; + } + + public void start() { + if (startTimeUs > endTimeUs) { + throw new RuntimeException("StartTimeUs(" + startTimeUs + ") must be less than or equal to EndTimeUs(" + endTimeUs + ")"); + } + + extractor.seekTo(startTimeUs, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); + decoder.start(); + + allInputExtracted = false; + decodingDone = false; + } + + public DecodedBufferData decode() { + + DecodedBufferData data = new DecodedBufferData(); + + boolean currentOutputDone = false; + while (!currentOutputDone && !decodingDone) { + + if (!allInputExtracted) { + int inBufferId = decoder.dequeueInputBuffer(TIMEOUT_USEC); + if (inBufferId >= 0) { + ByteBuffer buffer; + if (Build.VERSION.SDK_INT >= 21) { + buffer = decoder.getInputBuffer(inBufferId); + } else { + buffer = decoder.getInputBuffers()[inBufferId]; + } + int sampleSize = extractor.readSampleData(buffer, 0); + + if (sampleSize >= 0 && extractor.getSampleTime() <= endTimeUs) { + decoder.queueInputBuffer(inBufferId, 0, sampleSize, extractor.getSampleTime(), extractor.getSampleFlags()); + extractor.advance(); + } else { + if (loopingEnabled) { + decoder.flush(); + extractor.seekTo(startTimeUs, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); + } else { + decoder.queueInputBuffer(inBufferId, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); + allInputExtracted = true; + } + } + } + } + + + MediaCodec.BufferInfo outputBufferInfo = new MediaCodec.BufferInfo(); + int outputBufferIndex = decoder.dequeueOutputBuffer(outputBufferInfo, TIMEOUT_USEC); + + if (outputBufferIndex >= 0) { + if (Build.VERSION.SDK_INT >= 21) { + data.byteBuffer = decoder.getOutputBuffer(outputBufferIndex); + } else { + data.byteBuffer = decoder.getOutputBuffers()[outputBufferIndex]; + } + data.index = outputBufferIndex; + data.size = outputBufferInfo.size; + data.presentationTimeUs = outputBufferInfo.presentationTimeUs; + data.flags = outputBufferInfo.flags; + data.offset = outputBufferInfo.offset; + + // Adjusting start time + if (data.presentationTimeUs < startTimeUs) { + long timeDiff = startTimeUs - data.presentationTimeUs; + int bytesForTimeDiff = AudioConversions.usToBytes(timeDiff, getSampleRate(), getChannelCount()); + int position = data.byteBuffer.position() + bytesForTimeDiff; + if (position <= data.byteBuffer.limit()) { + data.byteBuffer.position(position); + } + } + + // Adjusting end time + long nextTime = data.presentationTimeUs + AudioConversions.bytesToUs(data.size, getSampleRate(), getChannelCount()); + if (nextTime > endTimeUs) { + int bytesToRemove = AudioConversions.usToBytes(nextTime - endTimeUs, getSampleRate(), getChannelCount()); + if (bytesToRemove > 0) { + int limit = data.byteBuffer.limit() - bytesToRemove; + if (limit >= data.byteBuffer.position()) { + data.byteBuffer.limit(limit); + } + } + } + + // Did we get all output from decoder? + if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) + decodingDone = true; + if (data.byteBuffer.remaining() > 0) currentOutputDone = true; + } + + } + + return data; + } + + /** + * @param index last decoded output buffer's index + *

+ * This method must be called each time after decoding and and using the ByteBuffer sample + */ + public void releaseOutputBuffer(int index) { + decoder.releaseOutputBuffer(index, false); + } + + public void stop() { + decoder.stop(); + decodingDone = true; + } + + public void release() { + stop(); + decoder.release(); + extractor.release(); + } + + public static class DecodedBufferData { + public ByteBuffer byteBuffer = null; + public int index = -1; + public int size = 0; + public long presentationTimeUs = 0; + public int flags = 0; + public int offset = 0; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/AudioRecoder.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/AudioRecoder.java index f5c4b4e9fb..931f4281d7 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/video/AudioRecoder.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/AudioRecoder.java @@ -3,191 +3,128 @@ import android.media.MediaCodec; import android.media.MediaExtractor; import android.media.MediaFormat; +import android.os.Build; + +import com.google.android.exoplayer2.util.Log; import org.telegram.messenger.FileLog; import org.telegram.messenger.MediaController; +import org.telegram.messenger.video.audio_input.AudioInput; +import org.telegram.messenger.video.audio_input.GeneralAudioInput; import java.io.IOException; import java.nio.ByteBuffer; +import java.nio.ShortBuffer; +import java.util.ArrayList; public class AudioRecoder { - private ByteBuffer[] decoderInputBuffers; - private ByteBuffer[] decoderOutputBuffers; + private static final int BYTES_PER_SHORT = 2; + private final int TIMEOUT_USEC = 2500; + private final int DEFAULT_SAMPLE_RATE = 44100; + private final int DEFAULT_BIT_RATE = 128000; + private final int DEFAULT_CHANNEL_COUNT = 2; + private ByteBuffer[] encoderInputBuffers; private ByteBuffer[] encoderOutputBuffers; - private final MediaCodec.BufferInfo decoderOutputBufferInfo = new MediaCodec.BufferInfo(); private final MediaCodec.BufferInfo encoderOutputBufferInfo = new MediaCodec.BufferInfo(); - private final MediaCodec decoder; + private final MediaCodec encoder; - private final MediaExtractor extractor; private boolean extractorDone = false; private boolean decoderDone = false; - private boolean encoderDone = false; + private boolean encoderInputDone = false; private int pendingAudioDecoderOutputBufferIndex = -1; - private final int trackIndex; - - private final int TIMEOUT_USEC = 2500; - - public long startTime = 0; - public long endTime = 0; + private int sampleRate = DEFAULT_SAMPLE_RATE; + private int channelCount = DEFAULT_CHANNEL_COUNT; public final MediaFormat format; - public AudioRecoder(MediaFormat inputAudioFormat, MediaExtractor extractor, int trackIndex) throws IOException { - this.extractor = extractor; - this.trackIndex = trackIndex; + AudioInput mainInput; + ArrayList audioInputs; + private long encoderInputPresentationTimeUs = 0; + private boolean encoderDone; + private long totalDurationUs; - decoder = MediaCodec.createDecoderByType(inputAudioFormat.getString(MediaFormat.KEY_MIME)); - decoder.configure(inputAudioFormat, null, null, 0); - decoder.start(); + public AudioRecoder(ArrayList audioInputs, long totalDurationUs) throws IOException { + this.audioInputs = audioInputs; + this.totalDurationUs = totalDurationUs; + mainInput = audioInputs.get(0); + for (int i = 0; i < audioInputs.size(); i++) { + if (audioInputs.get(i).getSampleRate() > sampleRate) { + sampleRate = audioInputs.get(i).getSampleRate(); + } + } encoder = MediaCodec.createEncoderByType(MediaController.AUIDO_MIME_TYPE); format = MediaFormat.createAudioFormat(MediaController.AUIDO_MIME_TYPE, - inputAudioFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE), - inputAudioFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) + sampleRate, + channelCount ); format.setInteger(MediaFormat.KEY_BIT_RATE, 64 * 1024); encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); encoder.start(); - - decoderInputBuffers = decoder.getInputBuffers(); - decoderOutputBuffers = decoder.getOutputBuffers(); encoderInputBuffers = encoder.getInputBuffers(); encoderOutputBuffers = encoder.getOutputBuffers(); + + for (int i = 0; i < audioInputs.size(); i++) { + audioInputs.get(i).start(sampleRate, channelCount); + } } public void release() { try { encoder.stop(); - decoder.stop(); - extractor.unselectTrack(trackIndex); - extractor.release(); + for (int i = 0; i < audioInputs.size(); i++) { + audioInputs.get(i).release(); + } } catch (Exception e) { FileLog.e(e); } } public boolean step(MP4Builder muxer, int audioTrackIndex) throws Exception { - while (!extractorDone) { - int decoderInputBufferIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC); - if (decoderInputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { - break; - } - - ByteBuffer decoderInputBuffer; - if (android.os.Build.VERSION.SDK_INT >= 21) { - decoderInputBuffer = decoder.getInputBuffer(decoderInputBufferIndex); - } else { - decoderInputBuffer = decoderInputBuffers[decoderInputBufferIndex]; - } - int size = extractor.readSampleData(decoderInputBuffer, 0); - - long presentationTime = extractor.getSampleTime(); - if (endTime > 0 && presentationTime >= endTime) { - encoderDone = true; - decoderOutputBufferInfo.flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM; - } - if (size >= 0) { - decoder.queueInputBuffer( - decoderInputBufferIndex, - 0, - size, - extractor.getSampleTime(), - extractor.getSampleFlags()); - } - - extractorDone = !extractor.advance(); - if (extractorDone) { - decoderInputBufferIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC); - decoder.queueInputBuffer( - decoderInputBufferIndex, - 0, - 0, - 0L, - MediaCodec.BUFFER_FLAG_END_OF_STREAM); - } - break; - } - - while (!decoderDone && pendingAudioDecoderOutputBufferIndex == -1) { - int decoderOutputBufferIndex = - decoder.dequeueOutputBuffer( - decoderOutputBufferInfo, TIMEOUT_USEC); - if (decoderOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { - break; - } - if (decoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { - decoderOutputBuffers = decoder.getOutputBuffers(); - break; + if (!encoderInputDone) { + int encoderBufferIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC); + if (encoderBufferIndex >= 0) { + if (isInputAvailable()) { + ShortBuffer encoderBuffer; + if (Build.VERSION.SDK_INT >= 21) { + encoderBuffer = encoder.getInputBuffer(encoderBufferIndex).asShortBuffer(); + } else { + encoderBuffer = encoder.getInputBuffers()[encoderBufferIndex].asShortBuffer(); + } + // mix the audios and add to encoder input buffer + mix(encoderBuffer); + + encoder.queueInputBuffer(encoderBufferIndex, + 0, + encoderBuffer.position() * BYTES_PER_SHORT, + encoderInputPresentationTimeUs, + MediaCodec.BUFFER_FLAG_KEY_FRAME); + encoderInputPresentationTimeUs += AudioConversions.shortsToUs(encoderBuffer.position(), sampleRate, channelCount); + } else { + encoder.queueInputBuffer(encoderBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); + encoderInputDone = true; + } } - if (decoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { - break; - } - - if ((decoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) - != 0) { - - decoder.releaseOutputBuffer(decoderOutputBufferIndex, false); - break; - } - pendingAudioDecoderOutputBufferIndex = decoderOutputBufferIndex; - - break; } - while (pendingAudioDecoderOutputBufferIndex != -1) { - int encoderInputBufferIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC); - if (encoderInputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { - break; - } - - ByteBuffer encoderInputBuffer = encoderInputBuffers[encoderInputBufferIndex]; - int size = decoderOutputBufferInfo.size; - long presentationTime = decoderOutputBufferInfo.presentationTimeUs; - if (size >= 0) { - ByteBuffer decoderOutputBuffer = - decoderOutputBuffers[pendingAudioDecoderOutputBufferIndex] - .duplicate(); - decoderOutputBuffer.position(decoderOutputBufferInfo.offset); - decoderOutputBuffer.limit(decoderOutputBufferInfo.offset + size); - encoderInputBuffer.position(0); - encoderInputBuffer.put(decoderOutputBuffer); - encoder.queueInputBuffer( - encoderInputBufferIndex, - 0, - size, - presentationTime, - decoderOutputBufferInfo.flags); - } - decoder.releaseOutputBuffer(pendingAudioDecoderOutputBufferIndex, false); - pendingAudioDecoderOutputBufferIndex = -1; - if ((decoderOutputBufferInfo.flags - & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { - decoderDone = true; - } - break; - } - - while (!encoderDone) { - int encoderOutputBufferIndex = encoder.dequeueOutputBuffer( - encoderOutputBufferInfo, TIMEOUT_USEC); + if (!encoderDone) { + int encoderOutputBufferIndex = encoder.dequeueOutputBuffer(encoderOutputBufferInfo, TIMEOUT_USEC); if (encoderOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { - break; + return encoderDone; } if (encoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { encoderOutputBuffers = encoder.getOutputBuffers(); - break; } if (encoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { - - break; + return encoderDone; } ByteBuffer encoderOutputBuffer = @@ -195,20 +132,50 @@ public boolean step(MP4Builder muxer, int audioTrackIndex) throws Exception { if ((encoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { encoder.releaseOutputBuffer(encoderOutputBufferIndex, false); - break; + return encoderDone; } if (encoderOutputBufferInfo.size != 0) { muxer.writeSampleData(audioTrackIndex, encoderOutputBuffer, encoderOutputBufferInfo, false); } - if ((encoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) - != 0) { + if ((encoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { encoderDone = true; } encoder.releaseOutputBuffer(encoderOutputBufferIndex, false); - break; } return encoderDone; } + + private void mix(ShortBuffer inputBuffer) { + final int size = inputBuffer.remaining(); + + for (int i = 0; i < size; i++) { + if (!isInputAvailable()) break; + + boolean put = false; + short result = 0; + + for (int j = 0; j < audioInputs.size(); j++) { + if (!isInputAvailable()) break; + + AudioInput input = audioInputs.get(j); + if (input.hasRemaining()) { + short value = input.getNext(); + //controlling volume + value = (short) (value * input.getVolume()); + result += value / audioInputs.size(); + put = true; + } + } + if (put) inputBuffer.put(result); + } + } + + private boolean isInputAvailable() { + if (encoderInputPresentationTimeUs > totalDurationUs) { + return false; + } + return mainInput.hasRemaining(); + } } 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 cec8e3a6c5..f6e5495f2f 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/video/MediaCodecVideoConvertor.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/MediaCodecVideoConvertor.java @@ -3,15 +3,12 @@ import android.annotation.TargetApi; import android.media.MediaCodec; import android.media.MediaCodecInfo; -import android.media.MediaCodecList; import android.media.MediaExtractor; 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; @@ -20,6 +17,9 @@ import org.telegram.messenger.SharedConfig; import org.telegram.messenger.Utilities; import org.telegram.messenger.VideoEditedInfo; +import org.telegram.messenger.video.audio_input.AudioInput; +import org.telegram.messenger.video.audio_input.BlankAudioInput; +import org.telegram.messenger.video.audio_input.GeneralAudioInput; import org.telegram.ui.Components.RLottieDrawable; import org.telegram.ui.Stories.recorder.StoryEntry; @@ -48,26 +48,9 @@ public class MediaCodecVideoConvertor { private static final int MEDIACODEC_TIMEOUT_INCREASED = 22000; private String outputMimeType; - public boolean convertVideo(String videoPath, File cacheFile, - int rotationValue, boolean isSecret, - int originalWidth, int originalHeight, - int resultWidth, int resultHeight, - int framerate, int bitrate, int originalBitrate, - long startTime, long endTime, long avatarStartTime, - boolean needCompress, long duration, - MediaController.SavedFilterState savedFilterState, - String paintPath, - ArrayList mediaEntities, - boolean isPhoto, - MediaController.CropState cropState, - boolean isRound, - 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, gradientTopColor, gradientBottomColor, muted, isStory, hdrInfo, parts, 0); + public boolean convertVideo(ConvertVideoParams convertStoryVideoParams) { + this.callback = convertStoryVideoParams.callback; + return convertVideoInternal(convertStoryVideoParams, false, 0); } public long getLastFrameTimestamp() { @@ -75,24 +58,38 @@ public long getLastFrameTimestamp() { } @TargetApi(18) - private boolean convertVideoInternal(String videoPath, File cacheFile, - int rotationValue, boolean isSecret, - int originalWidth, int originalHeight, - int resultWidth, int resultHeight, - int framerate, int bitrate, int originalBitrate, - long startTime, long endTime, long avatarStartTime, - long duration, - boolean needCompress, boolean increaseTimeout, - MediaController.SavedFilterState savedFilterState, - String paintPath, - ArrayList mediaEntities, - boolean isPhoto, - MediaController.CropState cropState, - boolean isRound, - Integer gradientTopColor, Integer gradientBottomColor, boolean muted, boolean isStory, - StoryEntry.HDRInfo hdrInfo, - ArrayList parts, + private boolean convertVideoInternal(ConvertVideoParams convertVideoParams, + boolean increaseTimeout, int triesCount) { + String videoPath = convertVideoParams.videoPath; + File cacheFile = convertVideoParams.cacheFile; + int rotationValue = convertVideoParams.rotationValue; + boolean isSecret = convertVideoParams.isSecret; + int originalWidth = convertVideoParams.originalWidth; + int originalHeight = convertVideoParams.originalHeight; + int resultWidth = convertVideoParams.resultWidth; + int resultHeight = convertVideoParams.resultHeight; + int framerate = convertVideoParams.framerate; + int bitrate = convertVideoParams.bitrate; + int originalBitrate = convertVideoParams.originalBitrate; + long startTime = convertVideoParams.startTime; + long endTime = convertVideoParams.endTime; + long avatarStartTime = convertVideoParams.avatarStartTime; + boolean needCompress = convertVideoParams.needCompress; + long duration = convertVideoParams.duration; + MediaController.SavedFilterState savedFilterState = convertVideoParams.savedFilterState; + String paintPath = convertVideoParams.paintPath; + String blurPath = convertVideoParams.blurPath; + ArrayList mediaEntities = convertVideoParams.mediaEntities; + boolean isPhoto = convertVideoParams.isPhoto; + MediaController.CropState cropState = convertVideoParams.cropState; + boolean isRound = convertVideoParams.isRound; + Integer gradientTopColor = convertVideoParams.gradientTopColor; + Integer gradientBottomColor = convertVideoParams.gradientBottomColor; + boolean muted = convertVideoParams.muted; + boolean isStory = convertVideoParams.isStory; + StoryEntry.HDRInfo hdrInfo = convertVideoParams.hdrInfo; + ArrayList parts = convertVideoParams.parts; FileLog.d("convertVideoInternal original=" + originalWidth + "x" + originalHeight + " result=" + resultWidth + "x" + resultHeight + " " + avatarStartTime); long time = System.currentTimeMillis(); @@ -123,6 +120,7 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, int prependHeaderSize = 0; endPresentationTime = duration * 1000; checkConversionCanceled(); + AudioRecoder audioRecoder = null; if (isPhoto) { try { @@ -180,7 +178,7 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, inputSurface.makeCurrent(); encoder.start(); - 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); + outputSurface = new OutputSurface(savedFilterState, videoPath, paintPath, blurPath, 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; @@ -193,9 +191,27 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, checkConversionCanceled(); mediaMuxer = new MP4Builder().createMovie(movie, isSecret, outputMimeType.equals("video/hevc")); - while (!outputDone) { + + int audioTrackIndex = -1; + boolean audioEncoderDone = true; + if (!convertVideoParams.soundInfos.isEmpty()) { + audioEncoderDone = false; + ArrayList audioInputs = new ArrayList<>(); + long totalDuration = duration * 1000; + BlankAudioInput mainInput = new BlankAudioInput(totalDuration); + audioInputs.add(mainInput); + applyAudioInputs(convertVideoParams.soundInfos, audioInputs); + + audioRecoder = new AudioRecoder(audioInputs, totalDuration); + audioTrackIndex = mediaMuxer.addTrack(audioRecoder.format, true); + } + while (!outputDone || !audioEncoderDone) { checkConversionCanceled(); + if (audioRecoder != null) { + audioEncoderDone = audioRecoder.step(mediaMuxer, audioTrackIndex); + } + boolean decoderOutputAvailable = !decoderDone; boolean encoderOutputAvailable = true; while (decoderOutputAvailable || encoderOutputAvailable) { @@ -330,6 +346,9 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, encoder.release(); encoder = null; } + if (audioRecoder != null) { + audioRecoder.release(); + } checkConversionCanceled(); } else { extractor = new MediaExtractor(); @@ -343,7 +362,6 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, } if (needCompress || needConvertVideo) { - AudioRecoder audioRecoder = null; ByteBuffer audioBuffer = null; boolean copyAudioBuffer = true; long lastFramePts = -1; @@ -439,7 +457,7 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, // prevent case when result video max 2MB outputFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR); } - outputFormat.setInteger( "max-bitrate", bitrate); + outputFormat.setInteger("max-bitrate", bitrate); outputFormat.setInteger(MediaFormat.KEY_FRAME_RATE, framerate); outputFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); @@ -475,7 +493,7 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, inputSurface.makeCurrent(); encoder.start(); - outputSurface = new OutputSurface(savedFilterState, null, paintPath, mediaEntities, cropState, resultWidth, resultHeight, originalWidth, originalHeight, rotationValue, framerate, false, gradientTopColor, gradientBottomColor, hdrInfo, parts); + outputSurface = new OutputSurface(savedFilterState, null, paintPath, blurPath, mediaEntities, cropState, resultWidth, resultHeight, originalWidth, originalHeight, rotationValue, framerate, false, gradientTopColor, gradientBottomColor, hdrInfo, parts); if (hdrInfo == null && outputSurface.supportsEXTYUV() && hasHDR) { hdrInfo = new StoryEntry.HDRInfo(); hdrInfo.colorTransfer = colorTransfer; @@ -487,15 +505,15 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, } 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 + 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 + createFragmentShader(originalWidth, originalHeight, resultWidth, resultHeight, true, isStory ? 0 : 3), + createFragmentShader(originalWidth, originalHeight, resultWidth, resultHeight, false, isStory ? 0 : 3), + false ); } decoder = getDecoderByFormat(videoFormat); @@ -514,7 +532,7 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, 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"); + copyAudioBuffer = convertVideoParams.soundInfos.isEmpty() && audioFormat.getString(MediaFormat.KEY_MIME).equals(MediaController.AUIDO_MIME_TYPE) || audioFormat.getString(MediaFormat.KEY_MIME).equals("audio/mpeg"); if (audioFormat.getString(MediaFormat.KEY_MIME).equals("audio/unknown")) { audioIndex = -1; @@ -540,25 +558,33 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, extractor.seekTo(0, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); } } else { - MediaExtractor audioExtractor = new MediaExtractor(); - audioExtractor.setDataSource(videoPath); - audioExtractor.selectTrack(audioIndex); - + ArrayList audioInputs = new ArrayList<>(); + GeneralAudioInput mainInput = new GeneralAudioInput(videoPath, audioIndex); + if (endTime > 0) { + mainInput.setEndTimeUs(endTime); + } if (startTime > 0) { - audioExtractor.seekTo(startTime, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); - } else { - audioExtractor.seekTo(0, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); + mainInput.setStartTimeUs(startTime); } + audioInputs.add(mainInput); + applyAudioInputs(convertVideoParams.soundInfos, audioInputs); - audioRecoder = new AudioRecoder(audioFormat, audioExtractor, audioIndex); - audioRecoder.startTime = startTime; - audioRecoder.endTime = endTime; + audioRecoder = new AudioRecoder(audioInputs, duration); audioTrackIndex = mediaMuxer.addTrack(audioRecoder.format, true); } } + } else if (!convertVideoParams.soundInfos.isEmpty()) { + copyAudioBuffer = false; + ArrayList audioInputs = new ArrayList<>(); + BlankAudioInput mainInput = new BlankAudioInput(duration); + audioInputs.add(mainInput); + applyAudioInputs(convertVideoParams.soundInfos, audioInputs); + + audioRecoder = new AudioRecoder(audioInputs, duration); + audioTrackIndex = mediaMuxer.addTrack(audioRecoder.format, true); } - boolean audioEncoderDone = audioIndex < 0; + boolean audioEncoderDone = audioRecoder == null; boolean firstEncode = true; @@ -568,7 +594,7 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, while (!outputDone || (!copyAudioBuffer && !audioEncoderDone)) { checkConversionCanceled(); - if (!copyAudioBuffer && audioRecoder != null) { + if (audioRecoder != null) { audioEncoderDone = audioRecoder.step(mediaMuxer, audioTrackIndex); } @@ -879,43 +905,39 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, if (encoder != null) { try { encoder.release(); - } catch (Exception ignore) {} + } catch (Exception ignore) { + } encoder = null; } if (decoder != null) { try { decoder.release(); - } catch (Exception ignore) {} + } catch (Exception ignore) { + } decoder = null; } if (outputSurface != null) { try { outputSurface.release(); - } catch (Exception ignore) {} + } catch (Exception ignore) { + } outputSurface = null; } if (inputSurface != null) { try { inputSurface.release(); - } catch (Exception ignore) {} + } catch (Exception ignore) { + } inputSurface = null; } } if (repeatWithIncreasedTimeout) { - return convertVideoInternal(videoPath, cacheFile, rotationValue, isSecret, - originalWidth, originalHeight, - resultWidth, resultHeight, framerate, bitrate, originalBitrate, startTime, endTime, avatarStartTime, duration, - needCompress, true, savedFilterState, paintPath, mediaEntities, - isPhoto, cropState, isRound, gradientTopColor, gradientBottomColor, muted, isStory, hdrInfo, parts, triesCount + 1); + return convertVideoInternal(convertVideoParams, true, 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); + return convertVideoInternal(convertVideoParams, increaseTimeout, triesCount + 1); } long timeLeft = System.currentTimeMillis() - time; @@ -926,6 +948,25 @@ private boolean convertVideoInternal(String videoPath, File cacheFile, return error; } + private static void applyAudioInputs(ArrayList soundInfos, ArrayList audioInputs) throws IOException { + for (int i = 0; i < soundInfos.size(); i++) { + MixedSoundInfo soundInfo = soundInfos.get(i); + GeneralAudioInput secondAudio = new GeneralAudioInput(soundInfo.audioFile); + secondAudio.setVolume(soundInfo.volume); + long startTimeLocal = 0; + if (soundInfo.startTime > 0) { + secondAudio.setStartOffsetUs(soundInfo.startTime); + } + if (soundInfo.audioOffset > 0) { + secondAudio.setStartTimeUs(startTimeLocal = soundInfo.audioOffset); + } + if (soundInfo.duration > 0) { + secondAudio.setEndTimeUs(startTimeLocal + soundInfo.duration); + } + audioInputs.add(secondAudio); + } + } + 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) { @@ -1169,11 +1210,11 @@ private static String hdrFragmentShader( shaderCode = shaderCode.replace("$dstHeight", dstHeight + ".0"); // TODO(@dkaraush): use minlum/maxlum return shaderCode + "\n" + - "in vec2 vTextureCoord;\n" + - "out vec4 fragColor;\n" + - "void main() {\n" + - " fragColor = TEX(vTextureCoord);\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" + @@ -1239,6 +1280,7 @@ private static String createFragmentShader( } } + public class ConversionCanceledException extends RuntimeException { public ConversionCanceledException() { @@ -1273,4 +1315,104 @@ private MediaCodec getDecoderByFormat(MediaFormat format) { throw new RuntimeException(exception); } + public static class ConvertVideoParams { + String videoPath; + File cacheFile; + int rotationValue; + boolean isSecret; + int originalWidth, originalHeight; + int resultWidth, resultHeight; + int framerate; + int bitrate; + int originalBitrate; + long startTime; + long endTime; + long avatarStartTime; + boolean needCompress; + long duration; + MediaController.SavedFilterState savedFilterState; + String paintPath; + String blurPath; + ArrayList mediaEntities; + boolean isPhoto; + MediaController.CropState cropState; + boolean isRound; + MediaController.VideoConvertorListener callback; + Integer gradientTopColor; + Integer gradientBottomColor; + boolean muted; + boolean isStory; + StoryEntry.HDRInfo hdrInfo; + ArrayList parts; + public ArrayList soundInfos = new ArrayList(); + + private ConvertVideoParams() { + + } + + public static ConvertVideoParams of(String videoPath, File cacheFile, + int rotationValue, boolean isSecret, + int originalWidth, int originalHeight, + int resultWidth, int resultHeight, + int framerate, int bitrate, int originalBitrate, + long startTime, long endTime, long avatarStartTime, + boolean needCompress, long duration, + MediaController.SavedFilterState savedFilterState, + String paintPath, String blurPath, + ArrayList mediaEntities, + boolean isPhoto, + MediaController.CropState cropState, + boolean isRound, + MediaController.VideoConvertorListener callback, + Integer gradientTopColor, Integer gradientBottomColor, + boolean muted, boolean isStory, StoryEntry.HDRInfo hdrInfo, + ArrayList parts) { + ConvertVideoParams params = new ConvertVideoParams(); + params.videoPath = videoPath; + params.cacheFile = cacheFile; + params.rotationValue = rotationValue; + params.isSecret = isSecret; + params.originalWidth = originalWidth; + params.originalHeight = originalHeight; + params.resultWidth = resultWidth; + params.resultHeight = resultHeight; + params.framerate = framerate; + params.bitrate = bitrate; + params.originalBitrate = originalBitrate; + params.startTime = startTime; + params.endTime = endTime; + params.avatarStartTime = avatarStartTime; + params.needCompress = needCompress; + params.duration = duration; + params.savedFilterState = savedFilterState; + params.paintPath = paintPath; + params.blurPath = blurPath; + params.mediaEntities = mediaEntities; + params.isPhoto = isPhoto; + params.cropState = cropState; + params.isRound = isRound; + params.callback = callback; + params.gradientTopColor = gradientTopColor; + params.gradientBottomColor = gradientBottomColor; + params.muted = muted; + params.isStory = isStory; + params.hdrInfo = hdrInfo; + params.parts = parts; + + return params; + } + } + + public static class MixedSoundInfo { + + final String audioFile; + public float volume = 1f; + public long audioOffset; + public long startTime; + public long duration; + + public MixedSoundInfo(String file) { + this.audioFile = file; + } + } } 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 6c65b9b9ad..6a9c203c31 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/video/OutputSurface.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/OutputSurface.java @@ -39,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, 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); + public OutputSurface(MediaController.SavedFilterState savedFilterState, String imagePath, String paintPath, String blurPath, 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, blurPath, mediaEntities, cropState, w, h, originalW, originalH, rotation, fps, photo, gradientTopColor, gradientBottomColor, hdrInfo, parts); mTextureRender.surfaceCreated(); mSurfaceTexture = new SurfaceTexture(mTextureRender.getTextureId()); mSurfaceTexture.setOnFrameAvailableListener(this); 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 f962143726..4989e14200 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/video/TextureRenderer.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/TextureRenderer.java @@ -61,6 +61,7 @@ import org.telegram.ui.Components.AnimatedEmojiDrawable; import org.telegram.ui.Components.AnimatedEmojiSpan; import org.telegram.ui.Components.AnimatedFileDrawable; +import org.telegram.ui.Components.BlurringShader; import org.telegram.ui.Components.EditTextEffects; import org.telegram.ui.Components.FilterShaders; import org.telegram.ui.Components.Paint.Views.EditTextOutline; @@ -89,6 +90,8 @@ public class TextureRenderer { private FloatBuffer renderTextureBuffer; private FloatBuffer bitmapVerticesBuffer; + private FloatBuffer blurVerticesBuffer; + private FloatBuffer partsVerticesBuffer[]; private FloatBuffer partsTextureBuffer; private ArrayList parts; @@ -105,7 +108,9 @@ public class TextureRenderer { private FilterShaders filterShaders; private String paintPath; + private String blurPath; private String imagePath; + private int imageWidth, imageHeight; private ArrayList mediaEntities; private ArrayList emojiDrawables; private int originalWidth; @@ -113,6 +118,8 @@ public class TextureRenderer { private int transformedWidth; private int transformedHeight; + private BlurringShader blur; + private static final String VERTEX_SHADER = "uniform mat4 uMVPMatrix;\n" + "uniform mat4 uSTMatrix;\n" + @@ -187,6 +194,12 @@ public class TextureRenderer { private int simpleInputTexCoordHandle; private int simpleSourceImageHandle; + private int blurShaderProgram; + private int blurPositionHandle; + private int blurInputTexCoordHandle; + private int blurBlurImageHandle; + private int blurMaskImageHandle; + private int[] paintTexture; private int[] stickerTexture; private Bitmap stickerBitmap; @@ -204,12 +217,16 @@ public class TextureRenderer { Paint xRefPaint; Paint textColorPaint; + private final MediaController.CropState cropState; + private int[] blurTexture; + private int gradientTopColor, gradientBottomColor; public TextureRenderer( MediaController.SavedFilterState savedFilterState, String image, String paint, + String blurtex, ArrayList entities, MediaController.CropState cropState, int w, int h, @@ -253,9 +270,7 @@ public TextureRenderer( if (savedFilterState != null) { filterShaders = new FilterShaders(true, hdrInfo); - if (savedFilterState != null) { - filterShaders.setDelegate(FilterShaders.getFilterShadersDelegate(savedFilterState)); - } + filterShaders.setDelegate(FilterShaders.getFilterShadersDelegate(savedFilterState)); } transformedWidth = w; transformedHeight = h; @@ -263,8 +278,10 @@ public TextureRenderer( this.originalHeight = originalHeight; imagePath = image; paintPath = paint; + blurPath = blurtex; mediaEntities = entities; videoFps = fps == 0 ? 30 : fps; + this.cropState = cropState; int count = 0; NUM_EXTERNAL_SHADER = count++; @@ -457,6 +474,7 @@ private void drawGradient() { } public void drawFrame(SurfaceTexture st) { + boolean blurred = false; if (isPhoto) { drawGradient(); } else { @@ -487,7 +505,7 @@ public void drawFrame(SurfaceTexture st) { filterShaders.drawEnhancePass(); filterShaders.drawSharpenPass(); filterShaders.drawCustomParamsPass(); - boolean blurred = filterShaders.drawBlurPass(); + blurred = filterShaders.drawBlurPass(); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); if (transformedWidth != originalWidth || transformedHeight != originalHeight) { @@ -524,6 +542,47 @@ public void drawFrame(SurfaceTexture st) { GLES20.glUniformMatrix4fv(muMVPMatrixHandle[index], 1, false, mMVPMatrix, 0); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); } + if (blur != null) { + if (!blendEnabled) { + GLES20.glEnable(GLES20.GL_BLEND); + GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA); + blendEnabled = true; + } + int tex = -1, w = 1, h = 1; + if (imagePath != null && paintTexture != null) { + tex = paintTexture[0]; + w = imageWidth; + h = imageHeight; + } else if (filterShaders != null) { + tex = filterShaders.getRenderTexture(blurred ? 0 : 1); + w = filterShaders.getRenderBufferWidth(); + h = filterShaders.getRenderBufferHeight(); + } + if (tex != -1) { + blur.draw(null, tex, w, h); + + GLES20.glViewport(0, 0, transformedWidth, transformedHeight); + + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + + GLES20.glUseProgram(blurShaderProgram); + + GLES20.glEnableVertexAttribArray(blurInputTexCoordHandle); + GLES20.glVertexAttribPointer(blurInputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, gradientTextureBuffer); + GLES20.glEnableVertexAttribArray(blurPositionHandle); + GLES20.glVertexAttribPointer(blurPositionHandle, 2, GLES20.GL_FLOAT, false, 8, blurVerticesBuffer); + + GLES20.glUniform1i(blurBlurImageHandle, 0); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, blur.getTexture()); + + GLES20.glUniform1i(blurMaskImageHandle, 1); + GLES20.glActiveTexture(GLES20.GL_TEXTURE1); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, blurTexture[0]); + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + } + } if (isPhoto || paintTexture != null || stickerTexture != null || partsTexture != null) { GLES20.glUseProgram(simpleShaderProgram); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); @@ -762,6 +821,82 @@ 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 (blurPath != null && cropState != null && cropState.useMatrix != null) { + blur = new BlurringShader(); + if (!blur.setup(transformedWidth / (float) transformedHeight, true, 0)) { + blur = null; + } else { + blur.updateGradient(gradientTopColor, gradientBottomColor); + android.graphics.Matrix matrix = new android.graphics.Matrix(); + matrix.postScale(originalWidth, originalHeight); + matrix.postConcat(cropState.useMatrix); + matrix.postScale(1f / transformedWidth, 1f / transformedHeight); + android.graphics.Matrix imatrix = new android.graphics.Matrix(); + matrix.invert(imatrix); + blur.updateTransform(imatrix); + } + + Bitmap bitmap = BitmapFactory.decodeFile(blurPath); + if (bitmap != null) { + + blurTexture = new int[1]; + GLES20.glGenTextures(1, blurTexture, 0); + GLES20.glBindTexture(GL10.GL_TEXTURE_2D, blurTexture[0]); + 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); + + bitmap.recycle(); + } else { + blur = null; + } + + if (blur != null) { + final String fragShader = + "varying highp vec2 vTextureCoord;" + + "uniform sampler2D blurImage;" + + "uniform sampler2D maskImage;" + + "void main() {" + + "gl_FragColor = texture2D(blurImage, vTextureCoord) * texture2D(maskImage, vTextureCoord).a;" + + "}"; + int vertexShader = FilterShaders.loadShader(GLES20.GL_VERTEX_SHADER, FilterShaders.simpleVertexShaderCode); + int fragmentShader = FilterShaders.loadShader(GLES20.GL_FRAGMENT_SHADER, fragShader); + + if (vertexShader != 0 && fragmentShader != 0) { + blurShaderProgram = GLES20.glCreateProgram(); + GLES20.glAttachShader(blurShaderProgram, vertexShader); + GLES20.glAttachShader(blurShaderProgram, fragmentShader); + GLES20.glBindAttribLocation(blurShaderProgram, 0, "position"); + GLES20.glBindAttribLocation(blurShaderProgram, 1, "inputTexCoord"); + + GLES20.glLinkProgram(blurShaderProgram); + int[] linkStatus = new int[1]; + GLES20.glGetProgramiv(blurShaderProgram, GLES20.GL_LINK_STATUS, linkStatus, 0); + if (linkStatus[0] == 0) { + GLES20.glDeleteProgram(blurShaderProgram); + blurShaderProgram = 0; + } else { + blurPositionHandle = GLES20.glGetAttribLocation(blurShaderProgram, "position"); + blurInputTexCoordHandle = GLES20.glGetAttribLocation(blurShaderProgram, "inputTexCoord"); + blurBlurImageHandle = GLES20.glGetUniformLocation(blurShaderProgram, "blurImage"); + blurMaskImageHandle = GLES20.glGetUniformLocation(blurShaderProgram, "maskImage"); + + float[] verticesData = { + -1.0f, 1.0f, + 1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, -1.0f, + }; + blurVerticesBuffer = ByteBuffer.allocateDirect(verticesData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer(); + blurVerticesBuffer.put(verticesData).position(0); + } + } else { + blur = 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); @@ -781,7 +916,7 @@ public void surfaceCreated() { } else { simplePositionHandle = GLES20.glGetAttribLocation(simpleShaderProgram, "position"); simpleInputTexCoordHandle = GLES20.glGetAttribLocation(simpleShaderProgram, "inputTexCoord"); - simpleSourceImageHandle = GLES20.glGetUniformLocation(simpleShaderProgram, "sourceImage"); + simpleSourceImageHandle = GLES20.glGetUniformLocation(simpleShaderProgram, "sTexture"); } } } @@ -827,6 +962,11 @@ public void surfaceCreated() { bitmap = newBitmap; } + if (a == 0 && imagePath != null) { + imageWidth = bitmap.getWidth(); + imageHeight = bitmap.getHeight(); + } + GLES20.glBindTexture(GL10.GL_TEXTURE_2D, paintTexture[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); @@ -905,7 +1045,6 @@ public void surfaceCreated() { } else if (entity.type == VideoEditedInfo.MediaEntity.TYPE_TEXT) { EditTextOutline editText = new EditTextOutline(ApplicationLoader.applicationContext); editText.getPaint().setAntiAlias(true); - editText.betterFraming = useMatrixForImagePath; editText.drawAnimatedEmojiDrawables = false; editText.setBackgroundColor(Color.TRANSPARENT); editText.setPadding(AndroidUtilities.dp(7), AndroidUtilities.dp(7), AndroidUtilities.dp(7), AndroidUtilities.dp(7)); @@ -928,7 +1067,7 @@ public void draw(@NonNull Canvas canvas, CharSequence charSequence, int start, i 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; + float tcy = entity.y + (editText.getPaddingTop() + top + (bottom - top) / 2f) / entity.viewHeight * entity.height; if (entity.rotation != 0) { float mx = entity.x + entity.width / 2f; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/VideoPlayerHolderBase.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/VideoPlayerHolderBase.java index 5e30a3317a..9bf10baca4 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/video/VideoPlayerHolderBase.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/VideoPlayerHolderBase.java @@ -188,7 +188,7 @@ public void onRenderedFirstFrame() { onReadyListener.run(); onReadyListener = null; } - }, 16); + }, surfaceView == null ? 16 : 32); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/audio_input/AudioInput.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/audio_input/AudioInput.java new file mode 100644 index 0000000000..6b59e3e7ab --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/audio_input/AudioInput.java @@ -0,0 +1,47 @@ +package org.telegram.messenger.video.audio_input; + +public abstract class AudioInput { + private boolean loopingEnabled; + private float volume = 1f; + + public boolean isLoopingEnabled() { + return loopingEnabled; + } + + public float getVolume() { + return volume; + } + + public void setLoopingEnabled(boolean loopingEnabled) { + this.loopingEnabled = loopingEnabled; + } + + public void setVolume(float volume) { + this.volume = Math.max(0f, Math.min(volume, 1f)); + } + + public abstract long getStartTimeUs(); + + public abstract long getEndTimeUs(); + + public abstract long getDurationUs(); + + public abstract int getSampleRate(); + + public abstract int getBitrate(); + + public abstract int getChannelCount(); + + public abstract boolean hasRemaining(); + + public abstract void setStartTimeUs(long timeUs); + + public abstract void setEndTimeUs(long timeUs); + + public abstract void start(int outputSampleRate, int outputChannelCount); + + public abstract short getNext(); + + public abstract void release(); + +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/audio_input/BlankAudioInput.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/audio_input/BlankAudioInput.java new file mode 100644 index 0000000000..8ac3957385 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/audio_input/BlankAudioInput.java @@ -0,0 +1,78 @@ +package org.telegram.messenger.video.audio_input; + +import org.telegram.messenger.video.AudioConversions; + +public class BlankAudioInput extends AudioInput { + + public final long durationUs; + private int requiredShortsForDuration; + private int remainingShorts; + + public BlankAudioInput(long durationUs){ + this.durationUs = durationUs; + } + + @Override + public long getStartTimeUs() { + return 0; + } + + @Override + public long getEndTimeUs() { + return durationUs; + } + + @Override + public long getDurationUs() { + return durationUs; + } + + @Override + public int getSampleRate() { + return -1; + } + + @Override + public int getBitrate() { + return -1; + } + + @Override + public int getChannelCount() { + return -1; + } + + @Override + public void setStartTimeUs(long timeUs) { } + + @Override + public void setEndTimeUs(long timeUs) { } + + @Override + public boolean hasRemaining() { + return remainingShorts > 0; + } + + @Override + public void start(int outputSampleRate, int outputChannelCount) { + requiredShortsForDuration = AudioConversions.usToShorts( + durationUs, outputSampleRate, outputChannelCount); + remainingShorts = requiredShortsForDuration; + } + + @Override + public short getNext() { + if(!hasRemaining()) throw new RuntimeException("Audio input has no remaining value."); + + remainingShorts--; + if(isLoopingEnabled() && remainingShorts == 0){ + remainingShorts = requiredShortsForDuration; + } + return 0; + } + + @Override + public void release() { + remainingShorts = 0; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/audio_input/GeneralAudioInput.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/audio_input/GeneralAudioInput.java new file mode 100644 index 0000000000..7f4e51e5b4 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/audio_input/GeneralAudioInput.java @@ -0,0 +1,154 @@ +package org.telegram.messenger.video.audio_input; + +import org.telegram.messenger.video.AudioBufferConverter; +import org.telegram.messenger.video.AudioConversions; +import org.telegram.messenger.video.AudioDecoder; + +import java.io.IOException; +import java.nio.ShortBuffer; + + +public class GeneralAudioInput extends AudioInput { + + private final AudioDecoder decoder; + private AudioBufferConverter audioBufferConverter; + + private long startOffsetUs; + private int requiredShortsForStartOffset; + private int startOffsetShortsCounter; + + private int outputSampleRate; + private int outputChannelCount; + + private ShortBuffer buffer; + private boolean hasRemaining; + + public GeneralAudioInput(String sourcePath) throws IOException { + decoder = new AudioDecoder(sourcePath); + init(); + } + + public GeneralAudioInput(String sourcePath, int audioIndex) throws IOException { + decoder = new AudioDecoder(sourcePath, audioIndex); + init(); + } + + private void init() { + audioBufferConverter = new AudioBufferConverter(); + } + + @Override + public void setLoopingEnabled(boolean loopingEnabled) { + super.setLoopingEnabled(loopingEnabled); + decoder.setLoopingEnabled(loopingEnabled); + } + + public long getStartOffsetUs() { + return startOffsetUs; + } + + @Override + public long getStartTimeUs() { + return decoder.getStartTimeUs(); + } + + @Override + public long getEndTimeUs() { + return decoder.getEndTimeUs(); + } + + @Override + public long getDurationUs() { + return getEndTimeUs() - getStartTimeUs() + getStartOffsetUs(); + } + + @Override + public int getSampleRate() { + return decoder.getSampleRate(); + } + + @Override + public int getBitrate() { + return decoder.getBitrateRate(); + } + + @Override + public int getChannelCount() { + return decoder.getChannelCount(); + } + + public void setStartOffsetUs(long startOffsetUs) { + this.startOffsetUs = startOffsetUs < 0 ? 0 : startOffsetUs; + } + + @Override + public void setStartTimeUs(long timeUs) { + decoder.setStartTimeUs(timeUs); + } + + @Override + public void setEndTimeUs(long timeUs) { + decoder.setEndTimeUs(timeUs); + } + + @Override + public boolean hasRemaining() { + return hasRemaining; + } + + @Override + public void start(int outputSampleRate, int outputChannelCount) { + this.outputSampleRate = outputSampleRate; + this.outputChannelCount = outputChannelCount; + hasRemaining = true; + decoder.start(); + + requiredShortsForStartOffset = AudioConversions.usToShorts(getStartOffsetUs(), this.outputSampleRate, this.outputChannelCount); + startOffsetShortsCounter = 0; + } + + @Override + public short getNext() { + if (!hasRemaining()) throw new RuntimeException("Audio input has no remaining value."); + + if (startOffsetShortsCounter < requiredShortsForStartOffset) { + startOffsetShortsCounter++; + return 0; + } + + decode(); + short value = 0; + if (buffer != null && buffer.remaining() > 0) { + value = buffer.get(); + } + decode(); + + if (buffer == null || buffer.remaining() < 1) { + hasRemaining = false; + } + + return value; + } + + private void decode() { + if (buffer == null || buffer.remaining() <= 0) { + AudioDecoder.DecodedBufferData audioData = decoder.decode(); + if (audioData.index >= 0) { + buffer = audioBufferConverter.convert(audioData.byteBuffer.asShortBuffer(), + decoder.getSampleRate(), decoder.getChannelCount(), + outputSampleRate, outputChannelCount); + decoder.releaseOutputBuffer(audioData.index); + } else { + buffer = null; + } + } + } + + @Override + public void release() { + buffer = null; + hasRemaining = false; + decoder.stop(); + decoder.release(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/remix/AudioRemixer.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/remix/AudioRemixer.java new file mode 100644 index 0000000000..3200e7f5c4 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/remix/AudioRemixer.java @@ -0,0 +1,39 @@ +package org.telegram.messenger.video.remix; + +import androidx.annotation.NonNull; + +import java.nio.Buffer; +import java.nio.ShortBuffer; + +/** + * Remixes audio data. See {@link DownMixAudioRemixer}, + * {@link UpMixAudioRemixer} or {@link PassThroughAudioRemixer} + * for concrete implementations. + */ +public interface AudioRemixer { + + /** + * Remixes input audio from input buffer into the output buffer. + * The output buffer is guaranteed to have a {@link Buffer#remaining()} size that is + * consistent with {@link #getRemixedSize(int,int,int)}. + * + * @param inputBuffer the input buffer + * @param outputBuffer the output buffer + */ + void remix(@NonNull final ShortBuffer inputBuffer, int inputChannelCount, + @NonNull final ShortBuffer outputBuffer, int outputChannelCount); + + /** + * Returns the output size (in shorts) needed to process an input buffer of the + * given size (in shorts). + * @param inputSize input size in shorts + * @return output size in shorts + */ + int getRemixedSize(int inputSize, int inputChannelCount, int outputChannelCount); + + AudioRemixer DOWNMIX = new DownMixAudioRemixer(); + + AudioRemixer UPMIX = new UpMixAudioRemixer(); + + AudioRemixer PASSTHROUGH = new PassThroughAudioRemixer(); +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/remix/DefaultAudioRemixer.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/remix/DefaultAudioRemixer.java new file mode 100644 index 0000000000..9f20f3329d --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/remix/DefaultAudioRemixer.java @@ -0,0 +1,34 @@ +package org.telegram.messenger.video.remix; + +import androidx.annotation.NonNull; + +import java.nio.ShortBuffer; + +public class DefaultAudioRemixer implements AudioRemixer { + + @Override + public void remix(@NonNull ShortBuffer inputBuffer, int inputChannelCount, @NonNull ShortBuffer outputBuffer, int outputChannelCount) { + AudioRemixer remixer; + if (inputChannelCount > outputChannelCount) { + remixer = DOWNMIX; + } else if (inputChannelCount < outputChannelCount) { + remixer = AudioRemixer.UPMIX; + } else { + remixer = AudioRemixer.PASSTHROUGH; + } + remixer.remix(inputBuffer, inputChannelCount, outputBuffer, outputChannelCount); + } + + @Override + public int getRemixedSize(int inputSize, int inputChannelCount, int outputChannelCount) { + AudioRemixer remixer; + if (inputChannelCount > outputChannelCount) { + remixer = DOWNMIX; + } else if (inputChannelCount < outputChannelCount) { + remixer = AudioRemixer.UPMIX; + } else { + remixer = AudioRemixer.PASSTHROUGH; + } + return remixer.getRemixedSize(inputSize, inputChannelCount, outputChannelCount); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/remix/DownMixAudioRemixer.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/remix/DownMixAudioRemixer.java new file mode 100644 index 0000000000..26062c547e --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/remix/DownMixAudioRemixer.java @@ -0,0 +1,49 @@ +package org.telegram.messenger.video.remix; + +import androidx.annotation.NonNull; + +import java.nio.ShortBuffer; + +/** + * A {@link AudioRemixer} that downmixes stereo audio to mono. + */ +public class DownMixAudioRemixer implements AudioRemixer { + + private static final int SIGNED_SHORT_LIMIT = 32768; + private static final int UNSIGNED_SHORT_MAX = 65535; + + @Override + public void remix(@NonNull ShortBuffer inputBuffer, int inputChannelCount, @NonNull ShortBuffer outputBuffer, int outputChannelCount) { + // Down-mix stereo to mono + // Viktor Toth's algorithm - + // See: http://www.vttoth.com/CMS/index.php/technical-notes/68 + // http://stackoverflow.com/a/25102339 + final int inRemaining = inputBuffer.remaining() / 2; + final int outSpace = outputBuffer.remaining(); + + final int samplesToBeProcessed = Math.min(inRemaining, outSpace); + for (int i = 0; i < samplesToBeProcessed; ++i) { + // Convert to unsigned + final int a = inputBuffer.get() + SIGNED_SHORT_LIMIT; + final int b = inputBuffer.get() + SIGNED_SHORT_LIMIT; + int m; + // Pick the equation + if ((a < SIGNED_SHORT_LIMIT) || (b < SIGNED_SHORT_LIMIT)) { + // Viktor's first equation when both sources are "quiet" + // (i.e. less than middle of the dynamic range) + m = a * b / SIGNED_SHORT_LIMIT; + } else { + // Viktor's second equation when one or both sources are loud + m = 2 * (a + b) - (a * b) / SIGNED_SHORT_LIMIT - UNSIGNED_SHORT_MAX; + } + // Convert output back to signed short + if (m == UNSIGNED_SHORT_MAX + 1) m = UNSIGNED_SHORT_MAX; + outputBuffer.put((short) (m - SIGNED_SHORT_LIMIT)); + } + } + + @Override + public int getRemixedSize(int inputSize, int inputChannelCount, int outputChannelCount) { + return inputSize / 2; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/remix/PassThroughAudioRemixer.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/remix/PassThroughAudioRemixer.java new file mode 100644 index 0000000000..4617002dbc --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/remix/PassThroughAudioRemixer.java @@ -0,0 +1,21 @@ +package org.telegram.messenger.video.remix; + +import androidx.annotation.NonNull; + +import java.nio.ShortBuffer; + +/** + * The simplest {@link AudioRemixer} that does nothing. + */ +public class PassThroughAudioRemixer implements AudioRemixer { + + @Override + public void remix(@NonNull ShortBuffer inputBuffer, int inputChannelCount, @NonNull ShortBuffer outputBuffer, int outputChannelCount) { + outputBuffer.put(inputBuffer); + } + + @Override + public int getRemixedSize(int inputSize, int inputChannelCount, int outputChannelCount) { + return inputSize; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/remix/UpMixAudioRemixer.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/remix/UpMixAudioRemixer.java new file mode 100644 index 0000000000..f77c9cd5bf --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/remix/UpMixAudioRemixer.java @@ -0,0 +1,30 @@ +package org.telegram.messenger.video.remix; + +import androidx.annotation.NonNull; + +import java.nio.ShortBuffer; + +/** + * A {@link AudioRemixer} that upmixes mono audio to stereo. + */ +public class UpMixAudioRemixer implements AudioRemixer { + + @Override + public void remix(@NonNull ShortBuffer inputBuffer, int inputChannelCount, @NonNull ShortBuffer outputBuffer, int outputChannelCount) { + // Up-mix mono to stereo + final int inRemaining = inputBuffer.remaining(); + final int outSpace = outputBuffer.remaining() / 2; + + final int samplesToBeProcessed = Math.min(inRemaining, outSpace); + for (int i = 0; i < samplesToBeProcessed; ++i) { + final short inSample = inputBuffer.get(); + outputBuffer.put(inSample); + outputBuffer.put(inSample); + } + } + + @Override + public int getRemixedSize(int inputSize, int inputChannelCount, int outputChannelCount) { + return inputSize * 2; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/resample/AudioResampler.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/resample/AudioResampler.java new file mode 100644 index 0000000000..54f7ac35f4 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/resample/AudioResampler.java @@ -0,0 +1,29 @@ +package org.telegram.messenger.video.resample; + +import androidx.annotation.NonNull; + +import java.nio.ShortBuffer; + +/** + * Resamples audio data. See {@link UpsampleAudioResampler} or + * {@link DownsampleAudioResampler} for concrete implementations. + */ +public interface AudioResampler { + + /** + * Resamples input audio from input buffer into the output buffer. + * + * @param inputBuffer the input buffer + * @param inputSampleRate the input sample rate + * @param outputBuffer the output buffer + * @param outputSampleRate the output sample rate + * @param channels the number of channels + */ + void resample(@NonNull final ShortBuffer inputBuffer, int inputSampleRate, @NonNull final ShortBuffer outputBuffer, int outputSampleRate, int channels); + + AudioResampler DOWNSAMPLE = new DownsampleAudioResampler(); + + AudioResampler UPSAMPLE = new UpsampleAudioResampler(); + + AudioResampler PASSTHROUGH = new PassThroughAudioResampler(); +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/resample/DefaultAudioResampler.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/resample/DefaultAudioResampler.java new file mode 100644 index 0000000000..fbb8f1cad7 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/resample/DefaultAudioResampler.java @@ -0,0 +1,23 @@ +package org.telegram.messenger.video.resample; + +import androidx.annotation.NonNull; + +import java.nio.ShortBuffer; + +/** + * An {@link AudioResampler} that delegates to appropriate classes + * based on input and output size. + */ +public class DefaultAudioResampler implements AudioResampler { + + @Override + public void resample(@NonNull ShortBuffer inputBuffer, int inputSampleRate, @NonNull ShortBuffer outputBuffer, int outputSampleRate, int channels) { + if (inputSampleRate < outputSampleRate) { + UPSAMPLE.resample(inputBuffer, inputSampleRate, outputBuffer, outputSampleRate, channels); + } else if (inputSampleRate > outputSampleRate) { + DOWNSAMPLE.resample(inputBuffer, inputSampleRate, outputBuffer, outputSampleRate, channels); + } else { + PASSTHROUGH.resample(inputBuffer, inputSampleRate, outputBuffer, outputSampleRate, channels); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/resample/DownsampleAudioResampler.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/resample/DownsampleAudioResampler.java new file mode 100644 index 0000000000..2dda9634c3 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/resample/DownsampleAudioResampler.java @@ -0,0 +1,47 @@ +package org.telegram.messenger.video.resample; + +import androidx.annotation.NonNull; + +import java.nio.ShortBuffer; + +/** + * An {@link AudioResampler} that downsamples from a higher sample rate to a lower sample rate. + */ +public class DownsampleAudioResampler implements AudioResampler { + + private static float ratio(int remaining, int all) { + return (float) remaining / all; + } + + @Override + public void resample(@NonNull ShortBuffer inputBuffer, int inputSampleRate, @NonNull ShortBuffer outputBuffer, int outputSampleRate, int channels) { + if (inputSampleRate < outputSampleRate) { + throw new IllegalArgumentException("Illegal use of DownsampleAudioResampler"); + } + if (channels != 1 && channels != 2) { + throw new IllegalArgumentException("Illegal use of DownsampleAudioResampler. Channels:" + channels); + } + final int inputSamples = inputBuffer.remaining() / channels; + final int outputSamples = (int) Math.ceil(inputSamples * ((double) outputSampleRate / inputSampleRate)); + final int dropSamples = inputSamples - outputSamples; + int remainingOutputSamples = outputSamples; + int remainingDropSamples = dropSamples; + float remainingOutputSamplesRatio = ratio(remainingOutputSamples, outputSamples); + float remainingDropSamplesRatio = ratio(remainingDropSamples, dropSamples); + while (remainingOutputSamples > 0 && remainingDropSamples > 0) { + // Will this be an input sample or a drop sample? + // Choose the one with the bigger ratio. + if (remainingOutputSamplesRatio >= remainingDropSamplesRatio) { + outputBuffer.put(inputBuffer.get()); + if (channels == 2) outputBuffer.put(inputBuffer.get()); + remainingOutputSamples--; + remainingOutputSamplesRatio = ratio(remainingOutputSamples, outputSamples); + } else { + // Drop this - read from input without writing. + inputBuffer.position(inputBuffer.position() + channels); + remainingDropSamples--; + remainingDropSamplesRatio = ratio(remainingDropSamples, dropSamples); + } + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/resample/PassThroughAudioResampler.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/resample/PassThroughAudioResampler.java new file mode 100644 index 0000000000..beee8cabed --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/resample/PassThroughAudioResampler.java @@ -0,0 +1,21 @@ +package org.telegram.messenger.video.resample; + +import androidx.annotation.NonNull; + +import java.nio.ShortBuffer; + +/** + * An {@link AudioResampler} that does nothing, meant to be used when sample + * rates are identical. + */ +public class PassThroughAudioResampler implements AudioResampler { + + @Override + public void resample(@NonNull ShortBuffer inputBuffer, int inputSampleRate, + @NonNull ShortBuffer outputBuffer, int outputSampleRate, int channels) { + if (inputSampleRate != outputSampleRate) { + throw new IllegalArgumentException("Illegal use of PassThroughAudioResampler"); + } + outputBuffer.put(inputBuffer); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/resample/UpsampleAudioResampler.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/resample/UpsampleAudioResampler.java new file mode 100644 index 0000000000..c3eb185f56 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/resample/UpsampleAudioResampler.java @@ -0,0 +1,72 @@ +package org.telegram.messenger.video.resample; + +import androidx.annotation.NonNull; + +import java.nio.ShortBuffer; + +/** + * An {@link AudioResampler} that upsamples from a lower sample rate to a higher sample rate. + */ +public class UpsampleAudioResampler implements AudioResampler { + + private static float ratio(int remaining, int all) { + return (float) remaining / all; + } + + @Override + public void resample(@NonNull ShortBuffer inputBuffer, int inputSampleRate, @NonNull ShortBuffer outputBuffer, int outputSampleRate, int channels) { + if (inputSampleRate > outputSampleRate) { + throw new IllegalArgumentException("Illegal use of UpsampleAudioResampler"); + } + if (channels != 1 && channels != 2) { + throw new IllegalArgumentException("Illegal use of UpsampleAudioResampler. Channels:" + channels); + } + final int inputSamples = inputBuffer.remaining() / channels; + final int outputSamples = (int) Math.ceil(inputSamples * ((double) outputSampleRate / inputSampleRate)); + final int fakeSamples = outputSamples - inputSamples; + int remainingInputSamples = inputSamples; + int remainingFakeSamples = fakeSamples; + float remainingInputSamplesRatio = ratio(remainingInputSamples, inputSamples); + float remainingFakeSamplesRatio = ratio(remainingFakeSamples, fakeSamples); + while (remainingInputSamples > 0 && remainingFakeSamples > 0) { + // Will this be an input sample or a fake sample? + // Choose the one with the bigger ratio. + if (remainingInputSamplesRatio >= remainingFakeSamplesRatio) { + outputBuffer.put(inputBuffer.get()); + if (channels == 2) outputBuffer.put(inputBuffer.get()); + remainingInputSamples--; + remainingInputSamplesRatio = ratio(remainingInputSamples, inputSamples); + } else { + outputBuffer.put(fakeSample(outputBuffer, inputBuffer, 1, channels)); + if (channels == 2) outputBuffer.put(fakeSample(outputBuffer, inputBuffer, 2, channels)); + remainingFakeSamples--; + remainingFakeSamplesRatio = ratio(remainingFakeSamples, fakeSamples); + } + } + } + + /** + * We have different options here. + * 1. Return a 0 sample. + * 2. Return a noise sample. + * 3. Return the previous sample for this channel. + * 4. Return an interpolated value between previous and next sample for this channel. + * + * None of this provides a real quality improvement, since the fundamental issue is that we + * can not achieve a higher quality by simply inserting fake samples each X input samples. + * A real upsampling algorithm should do something more intensive like interpolating between + * all values, not just the spare one. + * + * However this is probably beyond the scope of this library. + * + * @param output output buffer + * @param input output buffer + * @param channel current channel + * @param channels number of channels + * @return a fake sample + */ + @SuppressWarnings("unused") + private static short fakeSample(@NonNull ShortBuffer output, @NonNull ShortBuffer input, int channel, int channels) { + return output.get(output.position() - channels); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java b/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java index 06e2261658..32805baa78 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java @@ -80,7 +80,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 = 162; + public static final int LAYER = 163; public static class TL_stats_megagroupStats extends TLObject { public static int constructor = 0xef7ff916; @@ -5699,6 +5699,7 @@ public static class TL_authorization extends TLObject { public boolean password_pending; public boolean encrypted_requests_disabled; public boolean call_requests_disabled; + public boolean unconfirmed; public long hash; public String device_model; public String platform; @@ -5732,6 +5733,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { password_pending = (flags & 4) != 0; encrypted_requests_disabled = (flags & 8) != 0; call_requests_disabled = (flags & 16) != 0; + unconfirmed = (flags & 32) != 0; hash = stream.readInt64(exception); device_model = stream.readString(exception); platform = stream.readString(exception); @@ -5753,6 +5755,7 @@ public void serializeToStream(AbstractSerializedData stream) { flags = password_pending ? (flags | 4) : (flags &~ 4); flags = encrypted_requests_disabled ? (flags | 8) : (flags &~ 8); flags = call_requests_disabled ? (flags | 16) : (flags &~ 16); + flags = unconfirmed ? (flags | 32) : (flags &~ 32); stream.writeInt32(flags); stream.writeInt64(hash); stream.writeString(device_model); @@ -6544,6 +6547,9 @@ public static abstract class ChatInvite extends TLObject { public ArrayList participants = new ArrayList<>(); public Chat chat; public int expires; + public boolean verified; + public boolean scam; + public boolean fake; public static ChatInvite TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { ChatInvite result = null; @@ -6578,6 +6584,9 @@ public void readParams(AbstractSerializedData stream, boolean exception) { isPublic = (flags & 4) != 0; megagroup = (flags & 8) != 0; request_needed = (flags & 64) != 0; + verified = (flags & 128) != 0; + scam = (flags & 256) != 0; + fake = (flags & 512) != 0; title = stream.readString(exception); boolean hasAbout = (flags & 32) != 0; if (hasAbout) { @@ -6612,6 +6621,9 @@ public void serializeToStream(AbstractSerializedData stream) { flags = megagroup ? (flags | 8) : (flags &~ 8); flags = about != null ? (flags | 32) : (flags &~ 32); flags = request_needed ? (flags | 64) : (flags &~ 64); + flags = verified ? (flags | 128) : (flags &~ 128); + flags = scam ? (flags | 256) : (flags &~ 256); + flags = fake ? (flags | 512) : (flags &~ 512); stream.writeInt32(flags); stream.writeString(title); if (about != null) { @@ -12514,7 +12526,7 @@ public void serializeToStream(AbstractSerializedData stream) { } } } - + public static class WebPageAttribute extends TLObject { public int flags; @@ -12540,7 +12552,7 @@ public static WebPageAttribute TLdeserialize(AbstractSerializedData stream, int public static class TL_webPageAttributeStory extends WebPageAttribute { public static final int constructor = 0x939a4671; - + public long user_id; public int id; public TLRPC.StoryItem storyItem; @@ -21139,6 +21151,7 @@ public static abstract class WebPage extends TLObject { public Page cached_page; public int date; public ArrayList attributes = new ArrayList<>(); + public String displayedText;//custom public static WebPage TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { WebPage result = null; @@ -32954,6 +32967,9 @@ public static Update TLdeserialize(AbstractSerializedData stream, int constructo case 0x1b49ec6d: result = new TL_updateDraftMessage(); break; + case 0x8951abef: + result = new TL_updateNewAuthorization(); + break; case 0xa7848924: result = new TL_updateUserName(); break; @@ -33074,7 +33090,7 @@ public static Update TLdeserialize(AbstractSerializedData stream, int constructo case 0x922e6e10: result = new TL_updateReadChannelInbox(); break; - case 0x68c13933: + case 0xf8227181: result = new TL_updateReadMessagesContents(); break; case 0x7761198: @@ -34481,6 +34497,28 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_updateNewAuthorization extends Update { + public static int constructor = 0x8951abef; + + public int flags; + public boolean unconfirmed; + public long hash; + public int date; + public String device; + public String location; + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + unconfirmed = (flags & 1) != 0; + hash = stream.readInt64(exception); + if ((flags & 1) != 0) { + date = stream.readInt32(exception); + device = stream.readString(exception); + location = stream.readString(exception); + } + } + } + public static class TL_updateUserName extends Update { public static int constructor = 0xa7848924; @@ -35333,13 +35371,16 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_updateReadMessagesContents extends Update { - public static int constructor = 0x68c13933; + public static int constructor = 0xf8227181; + public int flags; public ArrayList messages = new ArrayList<>(); public int pts; public int pts_count; + public int date; public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); int magic = stream.readInt32(exception); if (magic != 0x1cb5c415) { if (exception) { @@ -35353,10 +35394,14 @@ public void readParams(AbstractSerializedData stream, boolean exception) { } pts = stream.readInt32(exception); pts_count = stream.readInt32(exception); + if ((flags & 1) != 0) { + date = stream.readInt32(exception); + } } public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); + stream.writeInt32(flags); stream.writeInt32(0x1cb5c415); int count = messages.size(); stream.writeInt32(count); @@ -35365,6 +35410,9 @@ public void serializeToStream(AbstractSerializedData stream) { } stream.writeInt32(pts); stream.writeInt32(pts_count); + if ((flags & 1) != 0) { + stream.writeInt32(date); + } } } @@ -53327,6 +53375,7 @@ public static class TL_account_changeAuthorizationSettings extends TLObject { public static int constructor = 0x40f48462; public int flags; + public boolean confirmed; public long hash; public boolean encrypted_requests_disabled; public boolean call_requests_disabled; @@ -53337,6 +53386,7 @@ public TLObject deserializeResponse(AbstractSerializedData stream, int construct public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); + flags = confirmed ? (flags | 8) : (flags &~ 8); stream.writeInt32(flags); stream.writeInt64(hash); if ((flags & 1) != 0) { @@ -65991,13 +66041,81 @@ public void serializeToStream(AbstractSerializedData stream) { } } - public static class TL_attachMenuBot extends AttachMenuBot { + public static class TL_attachMenuBot_layer162 extends TL_attachMenuBot { public static int constructor = 0xc8aa2cd2; + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + inactive = (flags & 1) != 0; + has_settings = (flags & 2) != 0; + request_write_access = (flags & 4) != 0; + bot_id = stream.readInt64(exception); + short_name = 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++) { + AttachMenuPeerType object = AttachMenuPeerType.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + peer_types.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++) { + TL_attachMenuBotIcon object = TL_attachMenuBotIcon.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + icons.add(object); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = inactive ? (flags | 1) : (flags &~ 1); + flags = has_settings ? (flags | 2) : (flags &~ 2); + flags = request_write_access ? (flags | 4) : (flags &~ 4); + stream.writeInt32(flags); + stream.writeInt64(bot_id); + stream.writeString(short_name); + stream.writeInt32(0x1cb5c415); + int count = peer_types.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + peer_types.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = icons.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + icons.get(a).serializeToStream(stream); + } + } + } + + public static class TL_attachMenuBot extends AttachMenuBot { + public static int constructor = 0xd90d8dfe; + public int flags; public boolean inactive; public boolean has_settings; public boolean request_write_access; + public boolean show_in_attach_menu; + public boolean show_in_side_menu; + public boolean side_menu_disclaimer_needed; public long bot_id; public String short_name; public ArrayList peer_types = new ArrayList<>(); @@ -66008,6 +66126,9 @@ public void readParams(AbstractSerializedData stream, boolean exception) { inactive = (flags & 1) != 0; has_settings = (flags & 2) != 0; request_write_access = (flags & 4) != 0; + show_in_attach_menu = (flags & 8) != 0; + show_in_side_menu = (flags & 16) != 0; + side_menu_disclaimer_needed = (flags & 32) != 0; bot_id = stream.readInt64(exception); short_name = stream.readString(exception); int magic = stream.readInt32(exception); @@ -66047,6 +66168,9 @@ public void serializeToStream(AbstractSerializedData stream) { flags = inactive ? (flags | 1) : (flags &~ 1); flags = has_settings ? (flags | 2) : (flags &~ 2); flags = request_write_access ? (flags | 4) : (flags &~ 4); + flags = show_in_attach_menu ? (flags | 8) : (flags &~ 8); + flags = show_in_side_menu ? (flags | 16) : (flags &~ 16); + flags = side_menu_disclaimer_needed ? (flags | 32) : (flags &~ 32); stream.writeInt32(flags); stream.writeInt64(bot_id); stream.writeString(short_name); @@ -66070,11 +66194,14 @@ public static abstract class AttachMenuBot extends TLObject { public static TL_attachMenuBot TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { TL_attachMenuBot result = null; switch (constructor) { - case 0xe93cb772: - result = new TL_attachMenuBot_layer140(); + case 0xd90d8dfe: + result = new TL_attachMenuBot(); break; case 0xc8aa2cd2: - result = new TL_attachMenuBot(); + result = new TL_attachMenuBot_layer162(); + break; + case 0xe93cb772: + result = new TL_attachMenuBot_layer140(); break; } if (result == null && exception) { @@ -66607,12 +66734,14 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_messages_requestSimpleWebView extends TLObject { - public static int constructor = 0x299bec8e; + public static int constructor = 0x1a46500a; public int flags; public boolean from_switch_webview; + public boolean from_side_menu; public InputUser bot; public String url; + public String start_param; public TL_dataJSON theme_params; public String platform; @@ -66623,9 +66752,15 @@ public TLObject deserializeResponse(AbstractSerializedData stream, int construct public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); flags = from_switch_webview ? (flags | 2) : (flags &~ 2); + flags = from_side_menu ? (flags | 4) : (flags &~ 4); stream.writeInt32(flags); bot.serializeToStream(stream); - stream.writeString(url); + if ((flags & 8) != 0) { + stream.writeString(url); + } + if ((flags & 16) != 0) { + stream.writeString(start_param); + } if ((flags & 1) != 0) { theme_params.serializeToStream(stream); } @@ -68751,10 +68886,10 @@ 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; @@ -69741,10 +69876,10 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(expire_date); } } - + public static class TL_mediaAreaCoordinates extends TLObject { public static final int constructor = 0x3d1ea4e; - + public double x; public double y; public double w; @@ -69783,7 +69918,7 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeDouble(rotation); } } - + public static class MediaArea extends TLObject { public TL_mediaAreaCoordinates coordinates; @@ -69809,10 +69944,10 @@ public static MediaArea TLdeserialize(AbstractSerializedData stream, int constru return result; } } - + public static class TL_mediaAreaVenue extends MediaArea { public static final int constructor = 0xbe82db9c; - + public GeoPoint geo; public String title; public String address; @@ -70225,7 +70360,7 @@ public void serializeToStream(AbstractSerializedData stream) { stealth_mode.serializeToStream(stream); } } - + public static class TL_stories_canSendStory extends TLObject { public static int constructor = 0xb100d45d; @@ -70437,15 +70572,15 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeBool(hidden); } } - + public static class TL_contacts_setBlocked extends TLObject { public static int constructor = 0x94c65c76; - + public int flags; public boolean my_stories_from; public ArrayList id = new ArrayList<>(); public int limit; - + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { return Bool.TLdeserialize(stream, constructor, exception); } @@ -70757,12 +70892,12 @@ public void serializeToStream(AbstractSerializedData stream) { 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); } 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 4c219b88c7..8d68da578f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java @@ -440,12 +440,7 @@ private void createTitleTextView(int i) { if (titleTextView[i] != null) { return; } - titleTextView[i] = new SimpleTextView(getContext()) { - @Override - public void setAlpha(float alpha) { - super.setAlpha(alpha); - } - }; + titleTextView[i] = new SimpleTextView(getContext()); titleTextView[i].setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); if (titleColorToSet != 0) { titleTextView[i].setTextColor(titleColorToSet); 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 2abc85ba1a..a8389b0005 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java @@ -496,7 +496,8 @@ public boolean isInBubbleMode() { @Override public void drawHeaderShadow(Canvas canvas, int alpha, int y) { - if (headerShadowDrawable != null) { + if (headerShadowDrawable != null && SharedConfig.drawActionBarShadow) { + alpha = alpha / 2; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (headerShadowDrawable.getAlpha() != alpha) { headerShadowDrawable.setAlpha(alpha); 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 908a4dbb4c..7e548aa409 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java @@ -349,6 +349,7 @@ public void setBackScaleY(float value) { } } + @Override public void setBackgroundDrawable(Drawable drawable) { backgroundColor = Color.WHITE; backgroundDrawable = drawable; 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 f95a6c0d4a..a7eafb241c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AlertDialog.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AlertDialog.java @@ -195,7 +195,7 @@ public void setBlurParams(float blurAlpha, boolean blurBehind, boolean blurBackg this.blurredBackground = blurBackground; } - private boolean supportsNativeBlur() { + protected boolean supportsNativeBlur() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && LaunchActivity.systemBlurEnabled; } @@ -305,10 +305,7 @@ public void show() { shownAt = System.currentTimeMillis(); } - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - + protected View inflateContent(boolean setContent) { LinearLayout containerView = new LinearLayout(getContext()) { private boolean inLayout; @@ -594,7 +591,9 @@ protected void dispatchDraw(Canvas canvas) { } } containerView.setFitsSystemWindows(Build.VERSION.SDK_INT >= 21); - setContentView(containerView); + if (setContent) { + setContentView(containerView); + } final boolean hasButtons = positiveButtonText != null || negativeButtonText != null || neutralButtonText != null; @@ -1113,6 +1112,13 @@ public void setTextColor(int color) { window.setAttributes(params); + return containerView; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + inflateContent(true); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded); } @@ -1472,7 +1478,11 @@ public Builder(Context context, Theme.ResourcesProvider resourcesProvider) { } public Builder(Context context, int progressViewStyle, Theme.ResourcesProvider resourcesProvider) { - alertDialog = new AlertDialog(context, progressViewStyle, resourcesProvider); + alertDialog = createAlertDialog(context, progressViewStyle, resourcesProvider); + } + + protected AlertDialog createAlertDialog(Context context, int progressViewStyle, Theme.ResourcesProvider resourcesProvider) { + return new AlertDialog(context, progressViewStyle, resourcesProvider); } public Context getContext() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AlertDialogDecor.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AlertDialogDecor.java new file mode 100644 index 0000000000..cbabb65380 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AlertDialogDecor.java @@ -0,0 +1,233 @@ +package org.telegram.ui.ActionBar; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.app.Activity; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.Rect; +import android.os.Build; +import android.util.TypedValue; +import android.view.ContextThemeWrapper; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.FrameLayout; + +import androidx.annotation.Nullable; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +import org.telegram.messenger.AndroidUtilities; + +public class AlertDialogDecor extends AlertDialog { + + private static final int[] ATTRS = new int[]{ + android.R.attr.windowEnterAnimation, + android.R.attr.windowExitAnimation + }; + private static final int DIM_DURATION = 300; + + private int resEnterAnimation; + private int resExitAnimation; + private View rootView; + private View contentView; + private View dimView; + private OnShowListener onShowListener; + private OnDismissListener onDismissListener; + private boolean isDismissed = false; + private long openDelay = 0; + private final Runnable showRunnable = () -> { + rootView.setVisibility(View.VISIBLE); + dimView.setAlpha(0); + contentView.startAnimation(AnimationUtils.loadAnimation(getContext(), resEnterAnimation)); + dimView.animate().setDuration(DIM_DURATION).alpha(1f).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (onShowListener != null) { + onShowListener.onShow(AlertDialogDecor.this); + } + } + }).start(); + }; + + public AlertDialogDecor(Context context, int progressStyle) { + super(context, progressStyle, null); + } + + public AlertDialogDecor(Context context, int progressStyle, Theme.ResourcesProvider resourcesProvider) { + super(context, progressStyle, resourcesProvider); + } + + private ViewGroup getDecorView() { + return ((ViewGroup) getActivity(getContext()).getWindow().getDecorView()); + } + + private void extractAnimations() { + TypedValue typedValue = new TypedValue(); + Resources.Theme theme = getContext().getTheme(); + theme.resolveAttribute(android.R.attr.windowAnimationStyle, typedValue, true); + TypedArray array = getContext().obtainStyledAttributes(typedValue.resourceId, ATTRS); + resEnterAnimation = array.getResourceId(0, -1); + resExitAnimation = array.getResourceId(1, -1); + array.recycle(); + } + + @Override + protected boolean supportsNativeBlur() { + return false; + } + + @Override + public void show() { + extractAnimations(); + setDismissDialogByButtons(true); + + contentView = inflateContent(false); + contentView.setClickable(true); + contentView.setFitsSystemWindows(false); + + WindowManager.LayoutParams params = getWindow().getAttributes(); + FrameLayout container = new FrameLayout(getContext()); + container.setOnClickListener(v -> dismiss()); + + dimView = new View(getContext()); + dimView.setBackgroundColor(Theme.multAlpha(Color.BLACK, params.dimAmount)); + container.addView(dimView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); + + final FrameLayout contentWrapper = new FrameLayout(getContext()); + contentWrapper.addView(contentView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT)); + + FrameLayout.LayoutParams containerParams = new FrameLayout.LayoutParams(params.width, FrameLayout.LayoutParams.WRAP_CONTENT); + containerParams.gravity = Gravity.CENTER; + container.addView(contentWrapper, containerParams); + + rootView = container; + getDecorView().addView(rootView); + ViewCompat.requestApplyInsets(rootView); + ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, insets) -> { + final Rect rect = new Rect(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + final Insets r = insets.getInsets(WindowInsetsCompat.Type.ime() | WindowInsetsCompat.Type.systemBars()); + rect.set(r.left, r.top, r.right, r.bottom); + } else { + rect.set(insets.getStableInsetLeft(), insets.getStableInsetTop(), insets.getStableInsetRight(), insets.getStableInsetBottom()); + } + contentWrapper.setPadding(rect.left, rect.top, rect.right, rect.bottom); + contentWrapper.requestLayout(); + return insets; + }); + rootView.setVisibility(View.INVISIBLE); + + if (openDelay == 0) { + showRunnable.run(); + } else { + AndroidUtilities.runOnUIThread(showRunnable, openDelay); + } + } + + @Override + public void showDelayed(long delay) { + if (isShowing()) { + return; + } + openDelay = delay; + show(); + } + + @Override + public boolean isShowing() { + return getDecorView().indexOfChild(rootView) != -1 && !isDismissed; + } + + @Override + public void setOnShowListener(@Nullable OnShowListener listener) { + onShowListener = listener; + } + + @Override + public void setOnDismissListener(@Nullable OnDismissListener listener) { + onDismissListener = listener; + } + + @Override + public void dismiss() { + if (!isShowing()) { + return; + } + if (isDismissed) { + return; + } + isDismissed = true; + AndroidUtilities.cancelRunOnUIThread(showRunnable); + if (rootView.getVisibility() != View.VISIBLE) { + getDecorView().removeView(rootView); + return; + } + + Animation animation = AnimationUtils.loadAnimation(getContext(), resExitAnimation); + animation.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + + } + + @Override + public void onAnimationEnd(Animation animation) { + contentView.setAlpha(0f); + } + + @Override + public void onAnimationRepeat(Animation animation) { + + } + }); + contentView.clearAnimation(); + contentView.startAnimation(animation); + dimView.animate().setListener(null).cancel(); + dimView.animate().setDuration(DIM_DURATION).alpha(0f).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + getDecorView().removeView(rootView); + if (onDismissListener != null) { + onDismissListener.onDismiss(AlertDialogDecor.this); + } + } + }).start(); + } + + private Activity getActivity(Context context) { + if (context instanceof Activity) { + return (Activity) context; + } else if (context instanceof ContextThemeWrapper) { + return getActivity(((ContextThemeWrapper) context).getBaseContext()); + } + return null; + } + + public static class Builder extends AlertDialog.Builder { + + protected Builder(AlertDialogDecor alert) { + super(alert); + } + + public Builder(Context context) { + super(context, null); + } + + public Builder(Context context, Theme.ResourcesProvider resourcesProvider) { + super(context, 0, resourcesProvider); + } + + @Override + protected AlertDialog createAlertDialog(Context context, int progressViewStyle, Theme.ResourcesProvider resourcesProvider) { + return new AlertDialogDecor(context, progressViewStyle, resourcesProvider); + } + } +} 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 3812f518cf..356b8e779c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java @@ -129,6 +129,7 @@ public void setAlpha(int alpha) { protected int behindKeyboardColor; private boolean canDismissWithSwipe = false; + private boolean canDismissWithTouchOutside = true; private boolean allowCustomAnimation = true; private boolean showWithoutAnimation; @@ -988,6 +989,23 @@ protected void onConfigurationChanged(Configuration newConfig) { container.requestApplyInsets(); } } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + Bulletin.addDelegate(this, new Bulletin.Delegate() { + @Override + public int getTopOffset(int tag) { + return AndroidUtilities.statusBarHeight; + } + }); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + Bulletin.removeDelegate(this); + } }; container.setBackgroundDrawable(backDrawable); focusable = needFocus; @@ -1335,7 +1353,11 @@ protected void onDismissWithTouchOutside() { } protected boolean canDismissWithTouchOutside() { - return true; + return canDismissWithTouchOutside; + } + + public void setCanDismissWithTouchOutside(boolean value) { + canDismissWithTouchOutside = value; } public TextView getTitleView() { 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 16d898761f..ac16717631 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DrawerLayoutContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DrawerLayoutContainer.java @@ -25,6 +25,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.os.Build; +import android.util.Log; import android.view.DisplayCutout; import android.view.Gravity; import android.view.MotionEvent; 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 d3bd4acc34..836eb15abe 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/EmojiThemes.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/EmojiThemes.java @@ -31,6 +31,7 @@ public class EmojiThemes { public TLRPC.WallPaper wallpaper; int currentIndex = 0; public ArrayList items = new ArrayList<>(); + private final int currentAccount; private static final int[] previewColorKeys = new int[]{ Theme.key_chat_inBubble, @@ -43,10 +44,12 @@ public class EmojiThemes { Theme.key_chat_wallpaper_gradient_rotation }; - public EmojiThemes() { + public EmojiThemes(int currentAccount) { + this.currentAccount = currentAccount; } - public EmojiThemes(TLRPC.TL_theme chatThemeObject, boolean isDefault) { + public EmojiThemes(int currentAccount, TLRPC.TL_theme chatThemeObject, boolean isDefault) { + this.currentAccount = currentAccount; this.showAsDefaultStub = isDefault; this.emoji = chatThemeObject.emoticon; if (!isDefault) { @@ -62,8 +65,8 @@ public EmojiThemes(TLRPC.TL_theme chatThemeObject, boolean isDefault) { } } - public static EmojiThemes createPreviewFullTheme(TLRPC.TL_theme tl_theme) { - EmojiThemes chatTheme = new EmojiThemes(); + public static EmojiThemes createPreviewFullTheme(int currentAccount, TLRPC.TL_theme tl_theme) { + EmojiThemes chatTheme = new EmojiThemes(currentAccount); chatTheme.emoji = tl_theme.emoticon; for (int i = 0; i < tl_theme.settings.size(); i++) { @@ -76,9 +79,9 @@ public static EmojiThemes createPreviewFullTheme(TLRPC.TL_theme tl_theme) { } - public static EmojiThemes createChatThemesDefault() { + public static EmojiThemes createChatThemesDefault(int currentAccount) { - EmojiThemes themeItem = new EmojiThemes(); + EmojiThemes themeItem = new EmojiThemes(currentAccount); themeItem.emoji = "❌"; themeItem.showAsDefaultStub = true; @@ -93,8 +96,8 @@ public static EmojiThemes createChatThemesDefault() { return themeItem; } - public static EmojiThemes createPreviewCustom() { - EmojiThemes themeItem = new EmojiThemes(); + public static EmojiThemes createPreviewCustom(int currentAccount) { + EmojiThemes themeItem = new EmojiThemes(currentAccount); themeItem.emoji = "\uD83C\uDFA8"; SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("themeconfig", Activity.MODE_PRIVATE); @@ -159,8 +162,8 @@ public static EmojiThemes createPreviewCustom() { return themeItem; } - public static EmojiThemes createHomePreviewTheme() { - EmojiThemes themeItem = new EmojiThemes(); + public static EmojiThemes createHomePreviewTheme(int currentAccount) { + EmojiThemes themeItem = new EmojiThemes(currentAccount); themeItem.emoji = "\uD83C\uDFE0"; ThemeItem blue = new ThemeItem(); @@ -185,8 +188,8 @@ public static EmojiThemes createHomePreviewTheme() { return themeItem; } - public static EmojiThemes createHomeQrTheme() { - EmojiThemes themeItem = new EmojiThemes(); + public static EmojiThemes createHomeQrTheme(int currentAccount) { + EmojiThemes themeItem = new EmojiThemes(currentAccount); themeItem.emoji = "\uD83C\uDFE0"; ThemeItem blue = new ThemeItem(); @@ -370,11 +373,11 @@ public void loadWallpaper(int index, ResultCallback> callback } long themeId = getTlTheme(index).id; - loadWallpaperImage(themeId, wallPaper, callback); + loadWallpaperImage(currentAccount, themeId, wallPaper, callback); } - public static void loadWallpaperImage(long hash, TLRPC.WallPaper wallPaper, ResultCallback> callback) { - ChatThemeController.getWallpaperBitmap(hash, cachedBitmap -> { + public static void loadWallpaperImage(int currentAccount, long hash, TLRPC.WallPaper wallPaper, ResultCallback> callback) { + ChatThemeController.getInstance(currentAccount).getWallpaperBitmap(hash, cachedBitmap -> { if (cachedBitmap != null && callback != null) { callback.onComplete(new Pair<>(hash, cachedBitmap)); return; @@ -401,7 +404,7 @@ public static void loadWallpaperImage(long hash, TLRPC.WallPaper wallPaper, Resu if (callback != null) { callback.onComplete(new Pair<>(hash, bitmap)); } - ChatThemeController.saveWallpaperBitmap(bitmap, hash); + ChatThemeController.getInstance(currentAccount).saveWallpaperBitmap(bitmap, hash); }); ImageLoader.getInstance().loadImageForImageReceiver(imageReceiver); }); @@ -417,7 +420,7 @@ public void loadWallpaperThumb(int index, ResultCallback> cal } long themeId = getTlTheme(index).id; - Bitmap bitmap = ChatThemeController.getWallpaperThumbBitmap(themeId); + Bitmap bitmap = ChatThemeController.getInstance(currentAccount).getWallpaperThumbBitmap(themeId); File file = getWallpaperThumbFile(themeId); if (bitmap == null && file.exists() && file.length() > 0) { try { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/RoundVideoShadow.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/RoundVideoShadow.java index f4eb6887be..c121b96488 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/RoundVideoShadow.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/RoundVideoShadow.java @@ -15,15 +15,9 @@ 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); } @@ -32,9 +26,6 @@ public RoundVideoShadow() { 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); } @@ -42,7 +33,6 @@ public void draw(@NonNull Canvas canvas) { @Override public void setAlpha(int alpha) { paint.setAlpha(alpha); - // eraserPaint.setAlpha(alpha); } @Override 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 bff0779416..a1a1ff9438 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java @@ -101,6 +101,7 @@ import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.SerializedData; import org.telegram.tgnet.TLRPC; +import org.telegram.ui.BlurSettingsBottomSheet; import org.telegram.ui.ChatActivity; import org.telegram.ui.Components.AudioVisualizerDrawable; import org.telegram.ui.Components.BackgroundGradientDrawable; @@ -3125,6 +3126,7 @@ public void run() { public static TextPaint chat_msgTextPaintTwoEmoji; public static TextPaint chat_msgTextPaintThreeEmoji; public static TextPaint chat_infoPaint; + public static TextPaint chat_infoBoldPaint; public static TextPaint chat_stickerCommentCountPaint; public static TextPaint chat_livePaint; public static TextPaint chat_docNamePaint; @@ -6345,6 +6347,7 @@ private static void applyTheme(ThemeInfo themeInfo, boolean save, boolean remove } catch (Exception e) { FileLog.e(e); } + BlurSettingsBottomSheet.onThemeApplyed(); if (previousTheme == null && save && !switchingNightTheme) { MessagesController.getInstance(themeInfo.account).saveTheme(themeInfo, themeInfo.getAccent(false), nightTheme, false); } @@ -8433,12 +8436,16 @@ public static void createCommonChatResources() { if (chat_infoPaint == null) { chat_infoPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + chat_infoBoldPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + chat_infoBoldPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); chat_stickerCommentCountPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); chat_stickerCommentCountPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); chat_docNamePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); chat_docNamePaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); chat_docBackPaint = new Paint(Paint.ANTI_ALIAS_FLAG); chat_deleteProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + chat_deleteProgressPaint.setStyle(Paint.Style.STROKE); + chat_deleteProgressPaint.setStrokeCap(Paint.Cap.ROUND); chat_locationTitlePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); chat_locationTitlePaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); chat_locationAddressPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); @@ -8670,7 +8677,7 @@ public static void createChatResources(Context context, boolean fontsOnly) { chat_filePath[1].lineTo(AndroidUtilities.dp(14), AndroidUtilities.dp(10)); chat_filePath[1].close(); - chat_flameIcon = resources.getDrawable(R.drawable.burn).mutate(); + chat_flameIcon = resources.getDrawable(R.drawable.filled_fire).mutate(); chat_gifIcon = resources.getDrawable(R.drawable.msg_round_gif_m).mutate(); chat_fileStatesDrawable[0][0] = createCircleDrawableWithIcon(AndroidUtilities.dp(44), R.drawable.msg_round_play_m); @@ -8750,6 +8757,7 @@ public static void createChatResources(Context context, boolean fontsOnly) { if (!fontsOnly && chat_infoPaint != null) { chat_infoPaint.setTextSize(AndroidUtilities.dp(12)); + chat_infoBoldPaint.setTextSize(AndroidUtilities.dp(12)); chat_stickerCommentCountPaint.setTextSize(AndroidUtilities.dp(11)); chat_docNamePaint.setTextSize(AndroidUtilities.dp(15)); chat_locationTitlePaint.setTextSize(AndroidUtilities.dp(15)); @@ -8832,7 +8840,6 @@ public static void applyChatTheme(boolean fontsOnly, boolean bg) { chat_botButtonPaint.setColor(getColor(key_chat_botButtonText)); chat_urlPaint.setColor(getColor(key_chat_linkSelectBackground)); chat_outUrlPaint.setColor(getColor(key_chat_outLinkSelectBackground)); - chat_deleteProgressPaint.setColor(getColor(key_chat_secretTimeText)); chat_textSearchSelectionPaint.setColor(getColor(key_chat_textSelectBackground)); chat_msgErrorPaint.setColor(getColor(key_chat_sentError)); chat_statusPaint.setColor(getColor(key_chat_status)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeColors.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeColors.java index 979aeb00f9..55ac5fdfa8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeColors.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeColors.java @@ -49,7 +49,7 @@ public static int[] createDefaultColors() { defaultColors[key_dialogButton] = 0xff4991cc; defaultColors[key_dialogButtonSelector] = 0x0f000000; defaultColors[key_dialogScrollGlow] = 0xfff5f6f7; - defaultColors[key_dialogRoundCheckBox] = 0xff4cb4f5; + defaultColors[key_dialogRoundCheckBox] = 0xff1A9CFF; defaultColors[key_dialogRoundCheckBoxCheck] = 0xffffffff; defaultColors[key_dialogCameraIcon] = 0xffffffff; defaultColors[key_dialog_inlineProgressBackground] = 0xf6f0f2f5; @@ -58,7 +58,7 @@ public static int[] createDefaultColors() { defaultColors[key_dialogSearchHint] = 0xff98a0a7; defaultColors[key_dialogSearchIcon] = 0xffa1a8af; defaultColors[key_dialogSearchText] = 0xff222222; - defaultColors[key_dialogFloatingButton] = 0xff4cb4f5; + defaultColors[key_dialogFloatingButton] = 0xff1A9CFF; defaultColors[key_dialogFloatingButtonPressed] = 0x0f000000; defaultColors[key_dialogFloatingIcon] = 0xffffffff; defaultColors[key_dialogShadowLine] = 0x12000000; 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 b9bbbb9a69..f641fae4fd 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsAdapter.java @@ -24,6 +24,7 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ContactsController; +import org.telegram.messenger.DialogObject; import org.telegram.messenger.FileLog; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessagesController; 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 c45120211f..fe12b540f6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsAdapter.java @@ -948,7 +948,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int i) { } TextView textView = cell.getTextView(); textView.setCompoundDrawablePadding(AndroidUtilities.dp(4)); - textView.setCompoundDrawablesWithIntrinsicBounds(null, null, arrowDrawable, null); + textView.setCompoundDrawablesWithIntrinsicBounds(null, null, parentFragment != null && parentFragment.storiesEnabled ? null : arrowDrawable, null); textView.getLayoutParams().width = LayoutHelper.WRAP_CONTENT; 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 ea7274b388..71c9c82599 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DrawerLayoutAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DrawerLayoutAdapter.java @@ -21,6 +21,7 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; @@ -348,17 +349,31 @@ private void resetItems() { peopleNearbyIcon = R.drawable.msg_nearby; } UserConfig me = UserConfig.getInstance(UserConfig.selectedAccount); + boolean showDivider = false; if (me != null && me.isPremium()) { if (me.getEmojiStatus() != null) { 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), R.drawable.msg_status_set)); } + showDivider = true; } + boolean needDivider = false; 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()) { + showDivider = true; + } + TLRPC.TL_attachMenuBots menuBots = MediaDataController.getInstance(UserConfig.selectedAccount).getAttachMenuBots(); + if (menuBots != null && menuBots.bots != null) { + for (int i = 0; i < menuBots.bots.size(); i++) { + TLRPC.TL_attachMenuBot bot = menuBots.bots.get(i); + if (bot.show_in_side_menu) { + items.add(new Item(bot)); + showDivider = true; + } + } + } + if (showDivider) { items.add(null); // divider } @@ -436,10 +451,23 @@ public CheckItem getItem(int position) { return item instanceof CheckItem ? (CheckItem) item : null; } + public TLRPC.TL_attachMenuBot getAttachMenuBot(int position) { + position -= 2; + if (accountsShown) { + position -= getAccountRowsCount(); + } + if (position < 0 || position >= items.size()) { + return null; + } + Item item = items.get(position); + return item != null ? item.bot : null; + } + private static class Item { public int icon; public String text; public int id; + TLRPC.TL_attachMenuBot bot; public Item(int id, String text, int icon) { this.icon = icon; @@ -447,8 +475,17 @@ public Item(int id, String text, int icon) { this.text = text; } + public Item(TLRPC.TL_attachMenuBot bot) { + this.bot = bot; + this.id = (int) (100 + (bot.bot_id >> 16)); + } + public void bind(DrawerActionCell actionCell) { - actionCell.setTextAndIcon(id, text, icon); + if (this.bot != null) { + actionCell.setBot(bot); + } else { + actionCell.setTextAndIcon(id, text, icon); + } } } 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 2ee60a1a28..af9ee4638f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java @@ -1777,7 +1777,7 @@ public void setDialogId(long dialogId) { dialog_id = dialogId; } - public void setUserOrChar(TLRPC.User user, TLRPC.Chat chat) { + public void setUserOrChat(TLRPC.User user, TLRPC.Chat chat) { this.user = user; this.chat = chat; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ArchiveSettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ArchiveSettingsActivity.java index 0bbf390009..64d2ecbc76 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ArchiveSettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ArchiveSettingsActivity.java @@ -246,7 +246,7 @@ public int getItemCount() { @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { - return holder.getItemViewType() != VIEW_TYPE_SHADOW; + return holder.getItemViewType() != VIEW_TYPE_SHADOW && holder.getItemViewType() != VIEW_TYPE_HEADER; } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java index e70d7668d0..f0e2f3243d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java @@ -65,8 +65,10 @@ import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; +import android.view.PixelCopy; import android.view.SoundEffectConstants; import android.view.Surface; +import android.view.SurfaceView; import android.view.TextureView; import android.view.VelocityTracker; import android.view.View; @@ -148,6 +150,7 @@ import org.telegram.ui.Components.AlertsCreator; import org.telegram.ui.Components.AnchorSpan; import org.telegram.ui.Components.AnimatedArrowDrawable; +import org.telegram.ui.Components.AnimatedFileDrawable; import org.telegram.ui.Components.AnimationProperties; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BulletinFactory; @@ -11716,6 +11719,7 @@ public void onClose() { public void onReleasePlayerBeforeClose(int photoIndex) { VideoPlayer player = PhotoViewer.getInstance().getVideoPlayer(); TextureView textureView = PhotoViewer.getInstance().getVideoTextureView(); + SurfaceView surfaceView = PhotoViewer.getInstance().getVideoSurfaceView(); BlockVideoCell videoCell = getViewFromListView(listView[0], pageBlocks.get(photoIndex)); if (videoCell != null && player != null && textureView != null) { videoCell.playFrom = player.getCurrentPosition(); @@ -11733,6 +11737,14 @@ public void onReleasePlayerBeforeClose(int photoIndex) { } } } + if (videoCell != null && player != null && surfaceView != null) { + videoCell.playFrom = player.getCurrentPosition(); + videoCell.firstFrameRendered = false; + videoCell.textureView.setAlpha(0); + Bitmap bitmap = Bitmap.createBitmap(surfaceView.getMeasuredWidth(), surfaceView.getMeasuredHeight(), Bitmap.Config.ARGB_8888); + AndroidUtilities.getBitmapFromSurface(surfaceView, bitmap); + videoCell.imageView.setImageBitmap(bitmap); + } checkVideoPlayer(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/BlurSettingsBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/BlurSettingsBottomSheet.java index 4f1e4415c6..88ed918771 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/BlurSettingsBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/BlurSettingsBottomSheet.java @@ -1,6 +1,7 @@ package org.telegram.ui; import android.content.Context; +import android.graphics.Color; import android.util.TypedValue; import android.view.Gravity; import android.view.View; @@ -8,29 +9,36 @@ import android.widget.ScrollView; import android.widget.TextView; +import androidx.fragment.app.Fragment; + import org.telegram.messenger.LocaleController; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.SeekBarView; +import org.telegram.ui.Components.SizeNotifierFrameLayout; public class BlurSettingsBottomSheet extends BottomSheet { BaseFragment fragment; - public static float saturation = 0.2f; + public static float saturation = 1f; public static float blurRadius = 1f; - public static float blurAlpha = 0.176f; + public static float blurAlpha = 1f - Color.alpha(Theme.getColor(Theme.key_chat_BlurAlpha)) / 255f; + SizeNotifierFrameLayout contentView; - public static void show(ChatActivity fragment) { + public static void show(BaseFragment fragment) { new BlurSettingsBottomSheet(fragment).show(); } - private BlurSettingsBottomSheet(ChatActivity fragment) { + private BlurSettingsBottomSheet(BaseFragment fragment) { super(fragment.getParentActivity(), false); this.fragment = fragment; + if (fragment.getFragmentView() instanceof SizeNotifierFrameLayout) { + contentView = (SizeNotifierFrameLayout) fragment.getFragmentView(); + } Context context = fragment.getParentActivity(); LinearLayout linearLayout = new LinearLayout(context); linearLayout.setOrientation(LinearLayout.VERTICAL); @@ -51,8 +59,8 @@ private BlurSettingsBottomSheet(ChatActivity fragment) { public void onSeekBarDrag(boolean stop, float progress) { saturation = progress; saturationTextView.setText("Saturation " + (progress * 5)); - fragment.contentView.invalidateBlurredViews(); - fragment.contentView.invalidateBlur(); + contentView.invalidateBlurredViews(); + contentView.invalidateBlur(); } @Override @@ -80,7 +88,7 @@ public void onSeekBarPressed(boolean pressed) { public void onSeekBarDrag(boolean stop, float progress) { alphaTextView.setText("Alpha " + blurAlpha); blurAlpha = progress; - fragment.contentView.invalidateBlur(); + contentView.invalidateBlur(); } @Override @@ -108,13 +116,13 @@ public void onSeekBarPressed(boolean pressed) { @Override public void onSeekBarDrag(boolean stop, float progress) { blurRadius = progress; - fragment.contentView.invalidateBlur(); - fragment.contentView.invalidateBlurredViews(); + contentView.invalidateBlur(); + contentView.invalidateBlurredViews(); } @Override public void onSeekBarPressed(boolean pressed) { - fragment.contentView.invalidateBlurredViews(); + contentView.invalidateBlurredViews(); } }); seekBar2.setReportChanges(true); @@ -134,4 +142,7 @@ public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int setCustomView(scrollView); } + public static void onThemeApplyed() { + blurAlpha = 1f - Color.alpha(Theme.getColor(Theme.key_chat_BlurAlpha, null, true)) / 255f; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/BubbleActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/BubbleActivity.java index 6c9b81afe8..997e153329 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/BubbleActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/BubbleActivity.java @@ -76,7 +76,7 @@ protected void onCreate(Bundle savedInstanceState) { SharedConfig.lastPauseTime = (int) (SystemClock.elapsedRealtime() / 1000); } - AndroidUtilities.fillStatusBarHeight(this); + AndroidUtilities.fillStatusBarHeight(this, false); Theme.createDialogsResources(this); Theme.createChatResources(this, false); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CacheControlActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/CacheControlActivity.java index cc3509d585..04aa253e52 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CacheControlActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CacheControlActivity.java @@ -1186,7 +1186,7 @@ private void cleanupFoldersInternal(Utilities.Callback2 onProgre cacheRemovedTooltip.setInfoText(LocaleController.formatString("CacheWasCleared", R.string.CacheWasCleared, AndroidUtilities.formatFileSize(finalClearedSize))); cacheRemovedTooltip.showWithAction(0, UndoView.ACTION_CACHE_WAS_CLEARED, null, null); }, 150); - MediaDataController.getInstance(currentAccount).chekAllMedia(true); + MediaDataController.getInstance(currentAccount).checkAllMedia(true); loadDialogEntities(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CalendarActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/CalendarActivity.java index 162091e1ad..a0ced6a750 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CalendarActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CalendarActivity.java @@ -165,7 +165,7 @@ public boolean onFragmentCreate() { 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); + storiesList = MessagesController.getInstance(currentAccount).getStoriesController().getStoriesList(dialogId, StoriesController.StoriesList.TYPE_ARCHIVE); } if (storiesList != null) { storiesPlaceProvider = new StoryViewer.PlaceProvider() { 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 7f8720cc82..2f862bd292 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java @@ -1091,9 +1091,9 @@ private void buildLayout() { } if (text == null) { if (messageObject.messageOwner != null && messageObject.messageOwner.media != null && messageObject.messageOwner.media.ttl_seconds != 0) { - if (messageObject.messageOwner.media.photo instanceof TLRPC.TL_photoEmpty) { + if (messageObject.messageOwner.media.photo != null) { text = LocaleController.getString("AttachPhotoExpired", R.string.AttachPhotoExpired); - } else if (messageObject.messageOwner.media.document instanceof TLRPC.TL_documentEmpty) { + } else if (messageObject.messageOwner.media.document instanceof TLRPC.TL_documentEmpty || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument && messageObject.messageOwner.media.document == null) { text = LocaleController.getString("AttachVideoExpired", R.string.AttachVideoExpired); } else { text = AnimatedEmojiSpan.cloneSpans(messageObject.messageText); 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 4fc947e0e4..b70f582065 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java @@ -24,6 +24,8 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Path; @@ -164,6 +166,7 @@ import org.telegram.ui.Components.VectorAvatarThumbDrawable; import org.telegram.ui.Components.VideoForwardDrawable; import org.telegram.ui.Components.spoilers.SpoilerEffect; +import org.telegram.ui.Components.spoilers.SpoilerEffect2; import org.telegram.ui.PhotoViewer; import org.telegram.ui.PinchToZoomHelper; import org.telegram.ui.SecretMediaViewer; @@ -201,6 +204,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate int parentBoundsBottom; public ExpiredStoryView expiredStoryView; + private boolean skipFrameUpdate; public RadialProgress2 getRadialProgress() { return radialProgress; @@ -386,7 +390,7 @@ public void markReactionsAsRead() { currentMessageObject.markReactionsAsRead(); } - public void setVisibleOnScreen(boolean visibleOnScreen) { + public void setVisibleOnScreen(boolean visibleOnScreen, float clipTop, float clipBottom) { if (this.visibleOnScreen != visibleOnScreen) { this.visibleOnScreen = visibleOnScreen; checkImageReceiversAttachState(); @@ -394,6 +398,16 @@ public void setVisibleOnScreen(boolean visibleOnScreen) { invalidate(); } } + clipTop -= photoImage.getImageY(); + clipBottom -= (getMeasuredHeight() - photoImage.getImageY2()); + float visibleImageHeight = photoImage.getImageHeight(); + if (clipTop > 0) { + visibleImageHeight -= clipTop; + } + if (clipBottom > 0) { + visibleImageHeight -= clipBottom; + } + photoImage.setSkipUpdateFrame(skipFrameUpdate = visibleImageHeight / photoImage.getImageHeight() < 0.25f); } public void setParentBounds(float chatListViewPaddingTop, int blurredViewBottomOffset) { @@ -726,6 +740,7 @@ public static class PollButton { private boolean drawRadialCheckBackground; private ImageReceiver photoImage; private ImageReceiver blurredPhotoImage; + private ColorMatrixColorFilter fancyBlurFilter; private AvatarDrawable contactAvatarDrawable; private Drawable locationLoadingThumb; private Drawable gradientDrawable; @@ -958,6 +973,7 @@ class LoadingDrawableLocation { private ArrayList lastPollResults; private int lastPollResultsVoters; private TimerParticles timerParticles; + private AnimatedFloat timerParticlesAlpha; private int pollHintX; private int pollHintY; private boolean pollHintPressed; @@ -1154,6 +1170,7 @@ class LoadingDrawableLocation { private float mediaSpoilerRevealX; private float mediaSpoilerRevealY; private float mediaSpoilerRevealMaxRadius; + private SpoilerEffect2 mediaSpoilerEffect2; private float unlockAlpha = 1f; private float unlockX; @@ -3131,7 +3148,11 @@ public boolean onTouchEvent(MotionEvent event) { playSoundEffect(SoundEffectConstants.CLICK); if (delegate != null) { if (currentForwardChannel != null) { - delegate.didPressChannelAvatar(this, currentForwardChannel, currentMessageObject.messageOwner.fwd_from.channel_post, lastTouchX, lastTouchY); + int postId = 0; + if (currentMessageObject.messageOwner.fwd_from != null) { + postId = currentMessageObject.messageOwner.fwd_from.channel_post; + } + delegate.didPressChannelAvatar(this, currentForwardChannel, postId, lastTouchX, lastTouchY); } else if (currentForwardUser != null) { delegate.didPressUserAvatar(this, currentForwardUser, lastTouchX, lastTouchY); } else if (currentForwardName != null) { @@ -3292,7 +3313,7 @@ private boolean checkPinchToZoom(MotionEvent ev) { isRoundVideo || currentMessageObject.isAnimatedSticker() || (currentMessageObject.isDocument() && !currentMessageObject.isGif()) || currentMessageObject.needDrawBluredPreview()) { return false; } - return pinchToZoomHelper.checkPinchToZoom(ev, this, photoImage, null, currentMessageObject); + return pinchToZoomHelper.checkPinchToZoom(ev, this, photoImage, null, currentMessageObject, mediaSpoilerEffect2 == null ? 0 : mediaSpoilerEffect2.getAttachIndex(this)); } private boolean checkTextSelection(MotionEvent event) { @@ -3636,7 +3657,7 @@ public static StaticLayout generateStaticLayout(CharSequence text, TextPaint pai } private void didClickedImage() { - if (currentMessageObject.hasMediaSpoilers() && !currentMessageObject.isMediaSpoilersRevealed) { + if (currentMessageObject.hasMediaSpoilers() && !currentMessageObject.needDrawBluredPreview() && !currentMessageObject.isMediaSpoilersRevealed) { startRevealMedia(lastTouchX, lastTouchY); return; } @@ -3733,13 +3754,18 @@ private void updateSecretTimeText(MessageObject messageObject) { if (messageObject == null || !messageObject.needDrawBluredPreview() || needHide) { return; } - String str = messageObject.getSecretTimeString(); + CharSequence str = messageObject.getSecretTimeString(); if (str == null) { return; } - infoWidth = (int) Math.ceil(Theme.chat_infoPaint.measureText(str)); - CharSequence str2 = TextUtils.ellipsize(str, Theme.chat_infoPaint, infoWidth, TextUtils.TruncateAt.END); - infoLayout = new StaticLayout(str2, Theme.chat_infoPaint, infoWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + if (str instanceof String) { + infoWidth = (int) Math.ceil(Theme.chat_infoPaint.measureText((String) str)); + CharSequence str2 = TextUtils.ellipsize(str, Theme.chat_infoPaint, infoWidth, TextUtils.TruncateAt.END); + infoLayout = new StaticLayout(str2, Theme.chat_infoPaint, infoWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + } else { + infoLayout = new StaticLayout(str, Theme.chat_infoBoldPaint, getMeasuredWidth() > 0 ? getMeasuredWidth() : 9999, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + infoWidth = infoLayout.getLineCount() > 0 ? (int) infoLayout.getLineWidth(0) : 0; + } invalidate(); } @@ -3905,6 +3931,10 @@ public ImageReceiver getPhotoImage() { return photoImage; } + public ImageReceiver getBlurredPhotoImage() { + return blurredPhotoImage; + } + public int getNoSoundIconCenterX() { return noSoundCenterX; } @@ -3987,6 +4017,10 @@ protected void onDetachedFromWindow() { if (currentNameStatusDrawable != null) { currentNameStatusDrawable.detach(); } + + if (mediaSpoilerEffect2 != null) { + mediaSpoilerEffect2.detach(this); + } } @Override @@ -4065,87 +4099,103 @@ public void onAttachedToWindow() { if (currentNameStatusDrawable != null) { currentNameStatusDrawable.attach(); } + if (mediaSpoilerEffect2 != null) { + if (mediaSpoilerEffect2.destroyed) { + mediaSpoilerEffect2 = SpoilerEffect2.getInstance(this); + } else { + mediaSpoilerEffect2.attach(this); + } + } } boolean imageReceiversAttachState; + boolean imageReceiversVisibleState; private void checkImageReceiversAttachState() { - boolean newAttachState = attachedToWindow && (visibleOnScreen || !shouldCheckVisibleOnScreen); - if (newAttachState == imageReceiversAttachState) { - return; - } - imageReceiversAttachState = newAttachState; - if (newAttachState) { - radialProgress.onAttachedToWindow(); - videoRadialProgress.onAttachedToWindow(); - if (pollAvatarImages != null) { - for (int a = 0; a < pollAvatarImages.length; a++) { - pollAvatarImages[a].onAttachedToWindow(); + boolean newAttachState = attachedToWindow; + boolean newVisibleState = (visibleOnScreen || !shouldCheckVisibleOnScreen); + if (newAttachState != imageReceiversAttachState) { + imageReceiversAttachState = newAttachState; + if (newAttachState) { + radialProgress.onAttachedToWindow(); + videoRadialProgress.onAttachedToWindow(); + if (pollAvatarImages != null) { + for (int a = 0; a < pollAvatarImages.length; a++) { + pollAvatarImages[a].onAttachedToWindow(); + } } - } - if (commentAvatarImages != null) { - for (int a = 0; a < commentAvatarImages.length; a++) { - commentAvatarImages[a].onAttachedToWindow(); + if (commentAvatarImages != null) { + for (int a = 0; a < commentAvatarImages.length; a++) { + commentAvatarImages[a].onAttachedToWindow(); + } } - } - replyImageReceiver.onAttachedToWindow(); - locationImageReceiver.onAttachedToWindow(); - blurredPhotoImage.onAttachedToWindow(); - if (photoImage.onAttachedToWindow()) { - if (drawPhotoImage) { + replyImageReceiver.onAttachedToWindow(); + locationImageReceiver.onAttachedToWindow(); + blurredPhotoImage.onAttachedToWindow(); + if (photoImage.onAttachedToWindow()) { + if (drawPhotoImage) { + updateButtonState(false, false, false); + } + } else { updateButtonState(false, false, false); } + animatedEmojiReplyStack = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, false, animatedEmojiReplyStack, replyTextLayout); + animatedEmojiDescriptionStack = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, false, animatedEmojiDescriptionStack, descriptionLayout); + updateAnimatedEmojis(); } else { - updateButtonState(false, false, false); - } - if (currentMessageObject != null && (isRoundVideo || currentMessageObject.isVideo())) { - checkVideoPlayback(true, null); - } - if (currentMessageObject != null && !currentMessageObject.mediaExists) { - int canDownload = DownloadController.getInstance(currentAccount).canDownloadMedia(currentMessageObject.messageOwner); - TLRPC.Document document = currentMessageObject.getDocument(); - boolean loadDocumentFromImageReceiver = MessageObject.isStickerDocument(document) || MessageObject.isAnimatedStickerDocument(document, true) || MessageObject.isGifDocument(document) || MessageObject.isRoundVideoDocument(document); - if (!loadDocumentFromImageReceiver) { - TLRPC.PhotoSize photo = document == null ? FileLoader.getClosestPhotoSizeWithSize(currentMessageObject.photoThumbs, AndroidUtilities.getPhotoSize()) : null; - if (canDownload == 2 || canDownload == 1 && currentMessageObject.isVideo()) { - if (canDownload != 2 && document != null && !currentMessageObject.shouldEncryptPhotoOrVideo() && currentMessageObject.canStreamVideo()) { - FileLoader.getInstance(currentAccount).loadFile(document, currentMessageObject, FileLoader.PRIORITY_NORMAL, 0); - } - } else if (canDownload != 0) { - if (document != null) { - FileLoader.getInstance(currentAccount).loadFile(document, currentMessageObject, FileLoader.PRIORITY_NORMAL, MessageObject.isVideoDocument(document) && currentMessageObject.shouldEncryptPhotoOrVideo() ? 2 : 0); - } else if (photo != null) { - FileLoader.getInstance(currentAccount).loadFile(ImageLocation.getForObject(photo, currentMessageObject.photoThumbsObject), currentMessageObject, null, FileLoader.PRIORITY_NORMAL, currentMessageObject.shouldEncryptPhotoOrVideo() ? 2 : 0); - } + radialProgress.onDetachedFromWindow(); + videoRadialProgress.onDetachedFromWindow(); + if (pollAvatarImages != null) { + for (int a = 0; a < pollAvatarImages.length; a++) { + pollAvatarImages[a].onDetachedFromWindow(); } - updateButtonState(false, false, false); - } - } - animatedEmojiReplyStack = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, false, animatedEmojiReplyStack, replyTextLayout); - animatedEmojiDescriptionStack = AnimatedEmojiSpan.update(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, this, false, animatedEmojiDescriptionStack, descriptionLayout); - updateAnimatedEmojis(); - } else { - radialProgress.onDetachedFromWindow(); - videoRadialProgress.onDetachedFromWindow(); - if (pollAvatarImages != null) { - for (int a = 0; a < pollAvatarImages.length; a++) { - pollAvatarImages[a].onDetachedFromWindow(); } - } - if (commentAvatarImages != null) { - for (int a = 0; a < commentAvatarImages.length; a++) { - commentAvatarImages[a].onDetachedFromWindow(); + if (commentAvatarImages != null) { + for (int a = 0; a < commentAvatarImages.length; a++) { + commentAvatarImages[a].onDetachedFromWindow(); + } + } + replyImageReceiver.onDetachedFromWindow(); + locationImageReceiver.onDetachedFromWindow(); + photoImage.onDetachedFromWindow(); + blurredPhotoImage.onDetachedFromWindow(); + + AnimatedEmojiSpan.release(this, animatedEmojiDescriptionStack); + AnimatedEmojiSpan.release(this, animatedEmojiReplyStack); + AnimatedEmojiSpan.release(this, animatedEmojiStack); + } + } + if (newVisibleState != imageReceiversVisibleState) { + imageReceiversVisibleState = newVisibleState; + if (newVisibleState) { + photoImage.setFileLoadingPriority(FileLoader.PRIORITY_NORMAL); + if (currentMessageObject != null && (isRoundVideo || currentMessageObject.isVideo())) { + checkVideoPlayback(true, null); + } + if (currentMessageObject != null && !currentMessageObject.mediaExists) { + int canDownload = DownloadController.getInstance(currentAccount).canDownloadMedia(currentMessageObject.messageOwner); + TLRPC.Document document = currentMessageObject.getDocument(); + boolean loadDocumentFromImageReceiver = MessageObject.isStickerDocument(document) || MessageObject.isAnimatedStickerDocument(document, true) || MessageObject.isGifDocument(document) || MessageObject.isRoundVideoDocument(document); + if (!loadDocumentFromImageReceiver) { + TLRPC.PhotoSize photo = document == null ? FileLoader.getClosestPhotoSizeWithSize(currentMessageObject.photoThumbs, AndroidUtilities.getPhotoSize()) : null; + if (canDownload == 2 || canDownload == 1 && currentMessageObject.isVideo()) { + if (canDownload != 2 && document != null && !currentMessageObject.shouldEncryptPhotoOrVideo() && currentMessageObject.canStreamVideo()) { + FileLoader.getInstance(currentAccount).loadFile(document, currentMessageObject, FileLoader.PRIORITY_NORMAL, 0); + } + } else if (canDownload != 0) { + if (document != null) { + FileLoader.getInstance(currentAccount).loadFile(document, currentMessageObject, FileLoader.PRIORITY_NORMAL, MessageObject.isVideoDocument(document) && currentMessageObject.shouldEncryptPhotoOrVideo() ? 2 : 0); + } else if (photo != null) { + FileLoader.getInstance(currentAccount).loadFile(ImageLocation.getForObject(photo, currentMessageObject.photoThumbsObject), currentMessageObject, null, FileLoader.PRIORITY_NORMAL, currentMessageObject.shouldEncryptPhotoOrVideo() ? 2 : 0); + } + } + updateButtonState(false, false, false); + } } + } else { + photoImage.setFileLoadingPriority(FileLoader.PRIORITY_LOW); + cancelLoading(currentMessageObject); } - replyImageReceiver.onDetachedFromWindow(); - locationImageReceiver.onDetachedFromWindow(); - photoImage.onDetachedFromWindow(); - blurredPhotoImage.onDetachedFromWindow(); - - cancelLoading(currentMessageObject); - AnimatedEmojiSpan.release(this, animatedEmojiDescriptionStack); - AnimatedEmojiSpan.release(this, animatedEmojiReplyStack); - AnimatedEmojiSpan.release(this, animatedEmojiStack); } } @@ -5701,6 +5751,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } if (photoImage.getBitmap() != null && !photoImage.getBitmap().isRecycled() && currentMessageObject.hasMediaSpoilers() && !currentMessageObject.isMediaSpoilersRevealed) { blurredPhotoImage.setImageBitmap(Utilities.stackBlurBitmapMax(photoImage.getBitmap())); + blurredPhotoImage.setColorFilter(getFancyBlurFilter()); } drawPhotoImage = true; @@ -6866,6 +6917,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } if (photoImage.getBitmap() != null && !photoImage.getBitmap().isRecycled() && currentMessageObject.hasMediaSpoilers() && !currentMessageObject.isMediaSpoilersRevealed) { blurredPhotoImage.setImageBitmap(Utilities.stackBlurBitmapMax(photoImage.getBitmap())); + blurredPhotoImage.setColorFilter(getFancyBlurFilter()); } } else { currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, AndroidUtilities.getPhotoSize()); @@ -7066,10 +7118,11 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe canChangeRadius = false; } else if (messageObject.needDrawBluredPreview() && !messageObject.hasExtendedMediaPreview()) { if (AndroidUtilities.isTablet()) { - w = h = (int) (AndroidUtilities.getMinTabletSide() * 0.5f); + w = (int) (AndroidUtilities.getMinTabletSide() * 0.6f); } else { - w = h = (int) (Math.min(getParentWidth(), AndroidUtilities.displaySize.y) * 0.5f); + w = (int) (Math.min(getParentWidth(), AndroidUtilities.displaySize.y) * 0.6f); } + h = (int) (0.61f * w); } int widthForCaption = 0; @@ -7126,7 +7179,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe boolean checkCaption = true; if ((currentPosition.flags & MessageObject.POSITION_FLAG_BOTTOM) != 0 || currentMessagesGroup.hasSibling && (currentPosition.flags & MessageObject.POSITION_FLAG_TOP) == 0) { widthForCaption += getAdditionalWidthForPosition(currentPosition); - int count = currentMessagesGroup.messages.size(); + int count = Math.min(currentMessagesGroup.posArray.size(), currentMessagesGroup.messages.size()); for (int i = 0; i < count; i++) { MessageObject m = currentMessagesGroup.messages.get(i); MessageObject.GroupedMessagePosition rowPosition = currentMessagesGroup.posArray.get(i); @@ -7217,7 +7270,6 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } else { captionLayout = new StaticLayout(currentCaption, Theme.chat_msgTextPaint, widthForCaption, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); } - updateCaptionSpoilers(); int lineCount = captionLayout.getLineCount(); if (lineCount > 0) { if (fixPhotoWidth) { @@ -7235,6 +7287,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } else { captionWidth = widthForCaption; } + updateCaptionSpoilers(); if (widthCaptionMin > 0 && captionWidth > widthCaptionMin) { photoWidth += captionWidth - widthCaptionMin; backgroundWidth += captionWidth - widthCaptionMin; @@ -7254,6 +7307,8 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe captionLayout = null; updateCaptionSpoilers(); } + } else { + updateCaptionSpoilers(); } } catch (Exception e) { FileLog.e(e); @@ -7333,6 +7388,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe messageObject.type == MessageObject.TYPE_ROUND_VIDEO ) { if (messageObject.needDrawBluredPreview()) { + photoImage.setColorFilter(getFancyBlurFilter()); currentPhotoFilter += "_b2"; currentPhotoFilterThumb += "_b2"; } else { @@ -7351,6 +7407,8 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe currentPhotoFilter = filterNew; } } + } else if (messageObject.needDrawBluredPreview()) { + photoImage.setColorFilter(getFancyBlurFilter()); } } @@ -7499,6 +7557,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } if (photoImage.getBitmap() != null && !photoImage.getBitmap().isRecycled() && currentMessageObject.hasMediaSpoilers() && !currentMessageObject.isMediaSpoilersRevealed) { blurredPhotoImage.setImageBitmap(Utilities.stackBlurBitmapMax(photoImage.getBitmap())); + blurredPhotoImage.setColorFilter(getFancyBlurFilter()); } setMessageObjectInternal(messageObject); @@ -7571,6 +7630,16 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } else { photoImage.setImageCoords(0, y + namesOffset + additionalTop, photoWidth, photoHeight); } + if (messageObject.hasMediaSpoilers() && SpoilerEffect2.supports()) { + if (mediaSpoilerEffect2 == null) { + mediaSpoilerEffect2 = SpoilerEffect2.getInstance(this); + } + } else { + if (mediaSpoilerEffect2 != null) { + mediaSpoilerEffect2.detach(this); + mediaSpoilerEffect2 = null; + } + } invalidate(); } @@ -8020,6 +8089,9 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe translationLoadingDrawable.reset(); translationLoadingDrawable = null; } + if (timerParticlesAlpha != null) { + timerParticlesAlpha.set(currentMessageObject != null && currentMessageObject.needDrawBluredPreview() && currentMessageObject.messageOwner != null && currentMessageObject.messageOwner.destroyTime != 0 && MediaController.getInstance().isPlayingMessage(currentMessageObject), true); + } transitionParams.lastStatusDrawableParams = -1; statusDrawableAnimationInProgress = false; @@ -9670,6 +9742,7 @@ private void drawContent(Canvas canvas) { forceNotDrawTime = currentMessagesGroup != null; photoImage.setPressed((isHighlightedAnimated || isHighlighted) && currentPosition != null ? 2 : 0); photoImage.setVisible(!PhotoViewer.isShowingImage(currentMessageObject) && !SecretMediaViewer.getInstance().isShowingImage(currentMessageObject) && !StoryViewer.isShowingImage(currentMessageObject), false); + blurredPhotoImage.setVisible(!PhotoViewer.isShowingImage(currentMessageObject) && !SecretMediaViewer.getInstance().isShowingImage(currentMessageObject) && !StoryViewer.isShowingImage(currentMessageObject), false); if (!photoImage.getVisible()) { mediaWasInvisible = true; timeWasInvisible = true; @@ -9842,7 +9915,7 @@ private void drawContent(Canvas canvas) { photoImageOutOfBounds = true; } if (!photoImageOutOfBounds || drawForBlur) { - photoImage.setSkipUpdateFrame(drawForBlur); + photoImage.setSkipUpdateFrame(drawForBlur || skipFrameUpdate); if (flipImage) { canvas.save(); canvas.scale(-1f, 1, photoImage.getCenterX(), photoImage.getCenterY()); @@ -9865,7 +9938,7 @@ private void drawContent(Canvas canvas) { drawBlurredPhoto(canvas); } } - photoImage.setSkipUpdateFrame(false); + photoImage.setSkipUpdateFrame(skipFrameUpdate); } } if (isRoundVideo && currentMessageObject.isVoiceTranscriptionOpen() && pipFloat > 0) { @@ -10468,17 +10541,26 @@ private void drawBlurredPhoto(Canvas canvas) { canvas.clipPath(mediaSpoilerPath, Region.Op.DIFFERENCE); } - blurredPhotoImage.setImageCoords(photoImage.getImageX(), photoImage.getImageY(), photoImage.getImageWidth(), photoImage.getImageHeight()); - blurredPhotoImage.setRoundRadius(photoImage.getRoundRadius()); - blurredPhotoImage.draw(canvas); - - int sColor = Color.WHITE; - mediaSpoilerEffect.setColor(ColorUtils.setAlphaComponent(sColor, (int) (Color.alpha(sColor) * 0.325f * photoImage.getAlpha()))); - mediaSpoilerEffect.setBounds((int) photoImage.getImageX(), (int) photoImage.getImageY(), (int) photoImage.getImageX2(), (int) photoImage.getImageY2()); - mediaSpoilerEffect.draw(canvas); - canvas.restore(); + if (currentMessageObject.needDrawBluredPreview()) { + photoImage.draw(canvas); + } else { + blurredPhotoImage.setImageCoords(photoImage.getImageX(), photoImage.getImageY(), photoImage.getImageWidth(), photoImage.getImageHeight()); + blurredPhotoImage.setRoundRadius(photoImage.getRoundRadius()); + blurredPhotoImage.draw(canvas); + } - invalidate(); + if (mediaSpoilerEffect2 != null) { + canvas.translate(photoImage.getImageX(), photoImage.getImageY()); + mediaSpoilerEffect2.draw(canvas, this, (int) photoImage.getImageWidth(), (int) photoImage.getImageHeight(), photoImage.getAlpha()); + canvas.restore(); + } else { + int sColor = Color.WHITE; + mediaSpoilerEffect.setColor(ColorUtils.setAlphaComponent(sColor, (int) (Color.alpha(sColor) * 0.325f * photoImage.getAlpha()))); + mediaSpoilerEffect.setBounds((int) photoImage.getImageX(), (int) photoImage.getImageY(), (int) photoImage.getImageX2(), (int) photoImage.getImageY2()); + mediaSpoilerEffect.draw(canvas); + canvas.restore(); + invalidate(); + } } private float getUseTranscribeButtonProgress() { @@ -11444,15 +11526,7 @@ private int getIconForCurrentState() { if (documentAttachType == DOCUMENT_ATTACH_TYPE_DOCUMENT) { return (drawPhotoImage && (currentPhotoObject != null || currentPhotoObjectThumb != null) && (photoImage.hasBitmapImage() || currentMessageObject.mediaExists || currentMessageObject.attachPathExists)) ? MediaActionDrawable.ICON_NONE : MediaActionDrawable.ICON_FILE; } else if (currentMessageObject.needDrawBluredPreview()) { - if (currentMessageObject.messageOwner.destroyTime != 0) { - if (currentMessageObject.isOutOwner()) { - return MediaActionDrawable.ICON_SECRETCHECK; - } else { - return MediaActionDrawable.ICON_EMPTY_NOPROGRESS; - } - } else { - return MediaActionDrawable.ICON_FIRE; - } + return MediaActionDrawable.ICON_FIRE; } else if (hasEmbed) { return MediaActionDrawable.ICON_PLAY; } @@ -12279,6 +12353,7 @@ public void didSetImage(ImageReceiver imageReceiver, boolean set, boolean thumb, } if (currentMessageObject.hasMediaSpoilers() && imageReceiver.getBitmap() != null && !imageReceiver.getBitmap().isRecycled()) { blurredPhotoImage.setImageBitmap(Utilities.stackBlurBitmapMax(imageReceiver.getBitmap())); + blurredPhotoImage.setColorFilter(getFancyBlurFilter()); } } } @@ -13510,7 +13585,6 @@ protected void onDraw(Canvas canvas) { if (!wasLayout) { onLayout(false, getLeft(), getTop(), getRight(), getBottom()); } - if (enterTransitionInProgress && currentMessageObject.isAnimatedEmojiStickers()) { return; } @@ -16254,13 +16328,31 @@ private void drawTimeInternal(Canvas canvas, float alpha, boolean fromParent, fl float timeHeight = Math.max(AndroidUtilities.dp(17), Theme.chat_timePaint.getTextSize() + AndroidUtilities.dp(5)); rect.set(x1, y1, x1 + timeWidth + AndroidUtilities.dp((bigRadius ? 12 : 8) + (currentMessageObject.isOutOwner() ? 20 + (currentMessageObject.type == MessageObject.TYPE_EMOJIS ? 4 : 0) : 0)), y1 + timeHeight); - applyServiceShaderMatrix(); - canvas.drawRoundRect(rect, r, r, paint); - if (paint == getThemedPaint(Theme.key_paint_chatActionBackground) && hasGradientService()) { - int oldAlpha2 = Theme.chat_actionBackgroundGradientDarkenPaint.getAlpha(); - Theme.chat_actionBackgroundGradientDarkenPaint.setAlpha((int) (oldAlpha2 * timeAlpha * alpha)); - canvas.drawRoundRect(rect, r, r, Theme.chat_actionBackgroundGradientDarkenPaint); - Theme.chat_actionBackgroundGradientDarkenPaint.setAlpha(oldAlpha2); + if (currentMessageObject.hasMediaSpoilers()) { + rectPath.rewind(); + rectPath.addRoundRect(rect, r, r, Path.Direction.CW); + canvas.save(); + canvas.clipPath(rectPath); + ImageReceiver imageReceiver = currentMessageObject.needDrawBluredPreview() ? photoImage : blurredPhotoImage; + float wasAlpha = imageReceiver.getAlpha(); + imageReceiver.setAlpha(.5f * wasAlpha); + imageReceiver.draw(canvas); + imageReceiver.setAlpha(wasAlpha); + canvas.restore(); + Paint dimPaint = getThemedPaint(Theme.key_paint_chatTimeBackground); + int oldAlpha2 = dimPaint.getAlpha(); + dimPaint.setAlpha((int) (oldAlpha2 * controlsAlpha * .4f)); + canvas.drawRoundRect(rect, r, r, dimPaint); + dimPaint.setAlpha(oldAlpha2); + } else { + applyServiceShaderMatrix(); + canvas.drawRoundRect(rect, r, r, paint); + if (paint == getThemedPaint(Theme.key_paint_chatActionBackground) && hasGradientService()) { + int oldAlpha2 = Theme.chat_actionBackgroundGradientDarkenPaint.getAlpha(); + Theme.chat_actionBackgroundGradientDarkenPaint.setAlpha((int) (oldAlpha2 * timeAlpha * alpha)); + canvas.drawRoundRect(rect, r, r, Theme.chat_actionBackgroundGradientDarkenPaint); + Theme.chat_actionBackgroundGradientDarkenPaint.setAlpha(oldAlpha2); + } } paint.setAlpha(oldAlpha); @@ -17093,7 +17185,7 @@ public void drawOverlays(Canvas canvas) { fullWidth = (currentPosition.flags & mask) == mask; } - if ((documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO || documentAttachType == DOCUMENT_ATTACH_TYPE_GIF) && (buttonState == 1 || buttonState == 2 || buttonState == 0 || buttonState == 3 || buttonState == -1 || currentMessageObject.needDrawBluredPreview())) { + if ((documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO || documentAttachType == DOCUMENT_ATTACH_TYPE_GIF) && (buttonState == 1 || buttonState == 2 || buttonState == 0 || buttonState == 3 || buttonState == -1) || currentMessageObject.needDrawBluredPreview()) { if (autoPlayingMedia) { updatePlayingMessageProgress(); } @@ -17149,17 +17241,31 @@ public void drawOverlays(Canvas canvas) { } canvas.save(); - canvas.scale(loadingProgressAlpha, loadingProgressAlpha, x1, y1); int oldAlpha = getThemedPaint(Theme.key_paint_chatTimeBackground).getAlpha(); - getThemedPaint(Theme.key_paint_chatTimeBackground).setAlpha((int) (oldAlpha * controlsAlpha * loadingProgressAlpha)); - if (drawDocTitleLayout || (drawLoadingProgress && loadingProgressLayout != null) || (!drawLoadingProgress && infoLayout != null)) { - rect.set(x1, y1, x1 + w, y1 + AndroidUtilities.dp(16.5f + 15.5f * alpha)); - int[] rad = photoImage.getRoundRadius(); - int r = Math.min(AndroidUtilities.dp(8), Math.max(rad[0], rad[1])); + getThemedPaint(Theme.key_paint_chatTimeBackground).setAlpha((int) (oldAlpha * controlsAlpha * loadingProgressAlpha * (currentMessageObject.needDrawBluredPreview() ? .4f : 1f))); + rect.set(x1, y1, x1 + w, y1 + AndroidUtilities.dp(16.5f + 15.5f * alpha)); + int[] rad = photoImage.getRoundRadius(); + int r = Math.min(AndroidUtilities.dp(8), Math.max(rad[0], rad[1])); + if (currentMessageObject.needDrawBluredPreview()) { + rectPath.reset(); + rectPath.addRoundRect(rect, r, r, Path.Direction.CW); + canvas.save(); + canvas.clipPath(rectPath); + float wasAlpha = photoImage.getAlpha(); + photoImage.setAlpha(.5f * wasAlpha); + photoImage.draw(canvas); + photoImage.setAlpha(wasAlpha); + canvas.restore(); canvas.drawRoundRect(rect, r, r, getThemedPaint(Theme.key_paint_chatTimeBackground)); + } else if (drawDocTitleLayout || (drawLoadingProgress && loadingProgressLayout != null) || (!drawLoadingProgress && infoLayout != null)) { + canvas.scale(loadingProgressAlpha, loadingProgressAlpha, x1, y1); + canvas.drawRoundRect(rect, r, r, getThemedPaint(Theme.key_paint_chatTimeBackground)); + } else { + canvas.scale(loadingProgressAlpha, loadingProgressAlpha, x1, y1); } Theme.chat_infoPaint.setAlpha((int) (255 * controlsAlpha * loadingProgressAlpha)); + Theme.chat_infoBoldPaint.setColor(Theme.chat_infoPaint.getColor()); canvas.translate(noSoundCenterX = (int) (photoImage.getImageX() + AndroidUtilities.dp((bigRadius ? 10 : 8) + (canStreamVideo ? 30 * alpha : 0))), photoImage.getImageY() + AndroidUtilities.dp(5.5f + 0.2f * alpha)); if (infoLayout != null && (!drawLoadingProgress || drawDocTitleLayout)) { @@ -17829,17 +17935,35 @@ public void drawOverlays(Canvas canvas) { restore = true; } if ((!isRoundVideo || !hasLinkPreview) && (!currentMessageObject.needDrawBluredPreview() || !MediaController.getInstance().isPlayingMessage(currentMessageObject)) && !(currentMessageObject.hasMediaSpoilers() && (!currentMessageObject.isMediaSpoilersRevealed || !currentMessageObject.revealingMediaSpoilers) && SharedConfig.isAutoplayVideo() && currentMessagesGroup == null && (radialProgress.getIcon() == MediaActionDrawable.ICON_PLAY || radialProgress.getIcon() == MediaActionDrawable.ICON_NONE))) { - if (isRoundVideo && !on) { + if (currentMessageObject.needDrawBluredPreview()) { + radialProgress.overrideCircleAlpha = 0f; + } else if (isRoundVideo && !on) { radialProgress.overrideCircleAlpha = .25f + .75f * (1f - getVideoTranscriptionProgress()); } if ((!SharedConfig.isAutoplayVideo() || currentMessagesGroup != null) && currentMessageObject.hasMediaSpoilers() && !currentMessageObject.isMediaSpoilersRevealed && radialProgress.getIcon() == MediaActionDrawable.ICON_PLAY) { canvas.saveLayerAlpha(radialProgress.getProgressRect(), (int) (mediaSpoilerRevealProgress * 0xFF), Canvas.ALL_SAVE_FLAG); } + if (currentMessageObject.needDrawBluredPreview()) { + rectPath.rewind(); + rectPath.addRoundRect(radialProgress.getProgressRect(), AndroidUtilities.dp(48), AndroidUtilities.dp(48), Path.Direction.CW); + canvas.save(); + canvas.clipPath(rectPath); + float wasAlpha = photoImage.getAlpha(); + photoImage.setAlpha(.5f * wasAlpha); + photoImage.draw(canvas); + photoImage.setAlpha(wasAlpha); + canvas.restore(); + Paint dimPaint = getThemedPaint(Theme.key_paint_chatTimeBackground); + int oldAlpha2 = dimPaint.getAlpha(); + dimPaint.setAlpha((int) (oldAlpha2 * controlsAlpha * .4f)); + canvas.drawRoundRect(radialProgress.getProgressRect(), AndroidUtilities.dp(48), AndroidUtilities.dp(48), dimPaint); + dimPaint.setAlpha(oldAlpha2); + } radialProgress.draw(canvas); if ((!SharedConfig.isAutoplayVideo() || currentMessagesGroup != null) && currentMessageObject.hasMediaSpoilers() && !currentMessageObject.isMediaSpoilersRevealed && radialProgress.getIcon() == MediaActionDrawable.ICON_PLAY) { canvas.restore(); } - if (isRoundVideo && !on) { + if (currentMessageObject.needDrawBluredPreview() || isRoundVideo && !on) { radialProgress.overrideCircleAlpha = 1f; } } @@ -17847,17 +17971,23 @@ public void drawOverlays(Canvas canvas) { canvas.restore(); } } - if (buttonState == -1 && currentMessageObject.needDrawBluredPreview() && !MediaController.getInstance().isPlayingMessage(currentMessageObject) && photoImage.getVisible() && currentMessageObject.messageOwner.destroyTime != 0) { - if (!currentMessageObject.isOutOwner()) { - long msTime = System.currentTimeMillis() + ConnectionsManager.getInstance(currentAccount).getTimeDifference() * 1000; - float progress = Math.max(0, (long) currentMessageObject.messageOwner.destroyTime * 1000 - msTime) / (currentMessageObject.messageOwner.ttl * 1000.0f); - Theme.chat_deleteProgressPaint.setAlpha((int) (255 * controlsAlpha)); - canvas.drawArc(deleteProgressRect, -90, -360 * progress, true, Theme.chat_deleteProgressPaint); - if (progress != 0) { - int offset = AndroidUtilities.dp(2); - invalidate((int) deleteProgressRect.left - offset, (int) deleteProgressRect.top - offset, (int) deleteProgressRect.right + offset * 2, (int) deleteProgressRect.bottom + offset * 2); - } + boolean drawCountdown = buttonState == -1 && currentMessageObject != null && currentMessageObject.needDrawBluredPreview() && !MediaController.getInstance().isPlayingMessage(currentMessageObject) && currentMessageObject.messageOwner.destroyTime != 0; + if (drawCountdown && timerParticlesAlpha == null) { + timerParticlesAlpha = new AnimatedFloat(0, this, 150, 120, CubicBezierInterpolator.EASE_OUT); + } + float countdownAlpha = timerParticlesAlpha == null ? 0 : timerParticlesAlpha.set(drawCountdown); + if (countdownAlpha > 0 && photoImage.getVisible()) { + final long msTime = System.currentTimeMillis() + ConnectionsManager.getInstance(currentAccount).getTimeDifference() * 1000; + final float progress = Math.max(0, (long) currentMessageObject.messageOwner.destroyTime * 1000 - msTime) / (currentMessageObject.messageOwner.ttl * 1000.0f); + Theme.chat_deleteProgressPaint.setColor(Color.WHITE); + Theme.chat_deleteProgressPaint.setAlpha((int) (0xFF * countdownAlpha * controlsAlpha)); + Theme.chat_deleteProgressPaint.setStrokeWidth(AndroidUtilities.dp(2)); + canvas.drawArc(deleteProgressRect, -90, -360 * progress, false, Theme.chat_deleteProgressPaint); + if (timerParticles == null) { + timerParticles = new TimerParticles(); } + timerParticles.draw(canvas, Theme.chat_deleteProgressPaint, deleteProgressRect, progress * -360, controlsAlpha); + invalidate(); updateSecretTimeText(currentMessageObject); } if ((drawVideoImageButton || animatingDrawVideoImageButton != 0) && photoImage.getVisible()) { @@ -19881,4 +20011,13 @@ public boolean hasGradientService() { return resourcesProvider != null ? resourcesProvider.hasGradientService() : Theme.hasGradientService(); } + private ColorMatrixColorFilter getFancyBlurFilter() { + if (fancyBlurFilter == null) { + ColorMatrix colorMatrix = new ColorMatrix(); + AndroidUtilities.multiplyBrightnessColorMatrix(colorMatrix, .9f); + AndroidUtilities.adjustSaturationColorMatrix(colorMatrix, +.6f); + fancyBlurFilter = new ColorMatrixColorFilter(colorMatrix); + } + return fancyBlurFilter; + } } 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 2c60ee7e42..666ac3bc19 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/CheckBoxCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/CheckBoxCell.java @@ -31,6 +31,7 @@ import org.telegram.ui.Components.CheckBoxSquare; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.LinkSpanDrawable; public class CheckBoxCell extends FrameLayout { @@ -42,7 +43,7 @@ public class CheckBoxCell extends FrameLayout { TYPE_CHECK_BOX_URL = 5; private final Theme.ResourcesProvider resourcesProvider; - private final TextView textView; + private final LinkSpanDrawable.LinksTextView textView; private final TextView valueTextView; private final View checkBox; private CheckBoxSquare checkBoxSquare; @@ -67,7 +68,7 @@ public CheckBoxCell(Context context, int type, int padding, Theme.ResourcesProvi this.resourcesProvider = resourcesProvider; this.currentType = type; - textView = new TextView(context) { + textView = new LinkSpanDrawable.LinksTextView(context) { @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); @@ -89,15 +90,15 @@ public void setText(CharSequence text, BufferType type) { textView.setEllipsize(TextUtils.TruncateAt.END); 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)); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 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 == 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)); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL, (LocaleController.isRTL ? 8 : 29), 0, (LocaleController.isRTL ? 29 : 8), 0)); } else { 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)); + addView(textView, LayoutHelper.createFrame(type == TYPE_CHECK_BOX_ROUND ? LayoutHelper.WRAP_CONTENT : LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL, (LocaleController.isRTL ? padding : offset + (padding - 17)), 0, (LocaleController.isRTL ? offset + (padding - 17) : padding), 0)); } } @@ -221,7 +222,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); 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)); + textView.measure(MeasureSpec.makeMeasureSpec(width - AndroidUtilities.dp(34), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(50), MeasureSpec.AT_MOST)); checkBox.measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(checkBoxSize), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(checkBoxSize), MeasureSpec.EXACTLY)); setMeasuredDimension(textView.getMeasuredWidth() + AndroidUtilities.dp(29), AndroidUtilities.dp(50)); @@ -236,7 +237,11 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } valueTextView.measure(MeasureSpec.makeMeasureSpec(availableWidth / 2, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); - textView.measure(MeasureSpec.makeMeasureSpec(availableWidth - (int) Math.abs(textView.getTranslationX()) - valueTextView.getMeasuredWidth() - AndroidUtilities.dp(8), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); + if (textView.getLayoutParams().width == LayoutHelper.MATCH_PARENT) { + textView.measure(MeasureSpec.makeMeasureSpec(availableWidth - (int) Math.abs(textView.getTranslationX()) - valueTextView.getMeasuredWidth() - AndroidUtilities.dp(8), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST)); + } else { + textView.measure(MeasureSpec.makeMeasureSpec(availableWidth - (int) Math.abs(textView.getTranslationX()) - valueTextView.getMeasuredWidth() - AndroidUtilities.dp(8), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST)); + } checkBox.measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(checkBoxSize), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(checkBoxSize), MeasureSpec.EXACTLY)); } 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 d01a97883d..06b443f1a4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java @@ -1448,7 +1448,7 @@ public void buildLayout() { } } else if (message.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto && message.messageOwner.media.photo instanceof TLRPC.TL_photoEmpty && message.messageOwner.media.ttl_seconds != 0) { messageString = LocaleController.getString("AttachPhotoExpired", R.string.AttachPhotoExpired); - } else if (message.messageOwner.media instanceof TLRPC.TL_messageMediaDocument && message.messageOwner.media.document instanceof TLRPC.TL_documentEmpty && message.messageOwner.media.ttl_seconds != 0) { + } else if (message.messageOwner.media instanceof TLRPC.TL_messageMediaDocument && (message.messageOwner.media.document instanceof TLRPC.TL_documentEmpty || message.messageOwner.media.document == null) && message.messageOwner.media.ttl_seconds != 0) { messageString = LocaleController.getString("AttachVideoExpired", R.string.AttachVideoExpired); } else if (getCaptionMessage() != null) { MessageObject message = getCaptionMessage(); @@ -1551,7 +1551,7 @@ public void buildLayout() { } } if (message.isReplyToStory()) { - SpannableStringBuilder builder = SpannableStringBuilder.valueOf(messageString); + SpannableStringBuilder builder = new SpannableStringBuilder(messageString); builder.insert(0, "d "); builder.setSpan(new ColoredImageSpan(ContextCompat.getDrawable(getContext(), R.drawable.msg_mini_replystory).mutate()), 0, 1, 0); messageString = builder; @@ -1587,7 +1587,7 @@ public void buildLayout() { } if (message.isForwarded()) { drawForwardIcon = true; - SpannableStringBuilder builder = SpannableStringBuilder.valueOf(messageString); + SpannableStringBuilder builder = new SpannableStringBuilder(messageString); builder.insert(0, "d "); ColoredImageSpan coloredImageSpan = new ColoredImageSpan(ContextCompat.getDrawable(getContext(), R.drawable.mini_forwarded).mutate()); coloredImageSpan.setAlpha(0.9f); @@ -3853,7 +3853,9 @@ protected void onDraw(Canvas canvas) { } if (avatarImage.getVisible()) { - needInvalidate = drawAvatarOverlays(canvas); + if (drawAvatarOverlays(canvas)) { + needInvalidate = true; + } } if (rightFragmentOpenedProgress > 0 && currentDialogFolderId == 0) { @@ -4033,6 +4035,8 @@ public boolean drawAvatarOverlays(Canvas canvas) { if (avatarImage.updateThumbShaderMatrix()) { if (avatarImage.thumbShader != null) { timerPaint.setShader(avatarImage.thumbShader); + } else if (avatarImage.staticThumbShader != null) { + timerPaint.setShader(avatarImage.staticThumbShader); } } else { timerPaint.setShader(null); 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 f18798daec..1a746316bc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerActionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerActionCell.java @@ -8,11 +8,15 @@ package org.telegram.ui.Cells; +import static org.telegram.ui.PremiumPreviewFragment.applyNewSpan; + import android.content.Context; import android.graphics.Canvas; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.text.SpannableStringBuilder; import android.util.TypedValue; import android.view.Gravity; import android.view.accessibility.AccessibilityNodeInfo; @@ -22,20 +26,26 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.FileLog; +import org.telegram.messenger.ImageLocation; import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; +import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RLottieImageView; +import org.telegram.ui.FilterCreateActivity; import java.util.Set; public class DrawerActionCell extends FrameLayout { - private ImageView imageView; + private BackupImageView imageView; private TextView textView; private int currentId; private RectF rect = new RectF(); @@ -43,7 +53,7 @@ public class DrawerActionCell extends FrameLayout { public DrawerActionCell(Context context) { super(context); - imageView = new ImageView(context); + imageView = new BackupImageView(context); imageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chats_menuItemIcon), PorterDuff.Mode.SRC_IN)); textView = new TextView(context); @@ -111,7 +121,7 @@ public void updateTextAndIcon(String text, int resId) { } } - public ImageView getImageView() { + public BackupImageView getImageView() { return imageView; } @@ -124,4 +134,32 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { info.setText(textView.getText()); info.setClassName(TextView.class.getName()); } + + public void setBot(TLRPC.TL_attachMenuBot bot) { + currentId = (int) bot.bot_id; + try { + if (bot.side_menu_disclaimer_needed) { + textView.setText(applyNewSpan(bot.short_name)); + } else { + textView.setText(bot.short_name); + } + TLRPC.TL_attachMenuBotIcon botIcon = MediaDataController.getSideMenuBotIcon(bot); + if (botIcon != null) { + imageView.setImage(ImageLocation.getForDocument(botIcon.icon), "24_24", (Drawable) null, bot); + } else { + imageView.setImageResource(R.drawable.msg_bot); + } + } catch (Throwable e) { + FileLog.e(e); + } + } + + public static CharSequence applyNewSpan(String str) { + SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(str); + spannableStringBuilder.append(" d"); + FilterCreateActivity.NewSpan span = new FilterCreateActivity.NewSpan(10); + span.setColor(Theme.getColor(Theme.key_premiumGradient1)); + spannableStringBuilder.setSpan(span, spannableStringBuilder.length() - 1, spannableStringBuilder.length(), 0); + return spannableStringBuilder; + } } 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 b9e3ffaf7b..d016777575 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java @@ -442,7 +442,7 @@ public void dispatchDraw(@NonNull Canvas canvas) { (int) ((getMeasuredHeight() + renderedEffectsSize) / 2f) ); effect.draw(canvas); - if (effect.done()) { + if (effect.isDone()) { effect.removeView(this); animations.remove(effect); } 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 be86c0f408..93c17c673c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachPhotoCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachPhotoCell.java @@ -57,6 +57,7 @@ import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.spoilers.SpoilerEffect; +import org.telegram.ui.Components.spoilers.SpoilerEffect2; import org.telegram.ui.PhotoViewer; public class PhotoAttachPhotoCell extends FrameLayout { @@ -86,6 +87,7 @@ public class PhotoAttachPhotoCell extends FrameLayout { private final Theme.ResourcesProvider resourcesProvider; private SpoilerEffect spoilerEffect = new SpoilerEffect(); + private SpoilerEffect2 spoilerEffect2; private boolean hasSpoiler; private Path path = new Path(); @@ -108,7 +110,30 @@ public PhotoAttachPhotoCell(Context context, Theme.ResourcesProvider resourcesPr setWillNotDraw(false); - container = new FrameLayout(context); + container = new FrameLayout(context) { + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (spoilerEffect2 != null && child == imageView) { + boolean r = super.drawChild(canvas, child, drawingTime); + if (hasSpoiler && spoilerRevealProgress != 1f && (photoEntry == null || !photoEntry.isAttachSpoilerRevealed)) { + if (spoilerRevealProgress != 0f) { + canvas.save(); + path.rewind(); + path.addCircle(spoilerRevealX, spoilerRevealY, spoilerMaxRadius * spoilerRevealProgress, Path.Direction.CW); + canvas.clipPath(path, Region.Op.DIFFERENCE); + } + float alphaProgress = CubicBezierInterpolator.DEFAULT.getInterpolation(1f - imageViewCrossfadeProgress); + float alpha = hasSpoiler ? alphaProgress : 1f - alphaProgress; + spoilerEffect2.draw(canvas, container, imageView.getMeasuredWidth(), imageView.getMeasuredHeight()); + if (spoilerRevealProgress != 0f) { + canvas.restore(); + } + } + return r; + } + return super.drawChild(canvas, child, drawingTime); + } + }; addView(container, LayoutHelper.createFrame(80, 80)); int sColor = Color.WHITE; @@ -141,8 +166,10 @@ protected void onDraw(Canvas canvas) { } blurImageReceiver.draw(canvas); - spoilerEffect.setBounds(0, 0, getWidth(), getHeight()); - spoilerEffect.draw(canvas); + if (spoilerEffect2 == null) { + spoilerEffect.setBounds(0, 0, getWidth(), getHeight()); + spoilerEffect.draw(canvas); + } invalidate(); if (spoilerRevealProgress != 0f) { @@ -158,6 +185,9 @@ protected void onDraw(Canvas canvas) { imageViewCrossfadeProgress = Math.min(1f, imageViewCrossfadeProgress + dt / duration); lastUpdate = System.currentTimeMillis(); invalidate(); + if (spoilerEffect2 != null) { + container.invalidate(); + } } else if (imageViewCrossfadeProgress == 1f && imageViewCrossfadeSnapshot != null) { imageViewCrossfadeSnapshot.recycle(); imageViewCrossfadeSnapshot = null; @@ -165,6 +195,12 @@ protected void onDraw(Canvas canvas) { invalidate(); } } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + updateSpoilers2(photoEntry != null && photoEntry.hasSpoiler); + } }; imageView.setBlurAllowed(true); container.addView(imageView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); @@ -258,6 +294,45 @@ public void setHasSpoiler(boolean hasSpoiler, Float crossfadeDuration) { this.crossfadeDuration = crossfadeDuration; imageView.setHasBlur(hasSpoiler); imageView.invalidate(); + if (hasSpoiler) { + updateSpoilers2(hasSpoiler); + } + } + } + + private void updateSpoilers2(boolean hasSpoiler) { + if (container == null || imageView == null || imageView.getMeasuredHeight() <= 0 || imageView.getMeasuredWidth() <= 0) { + return; + } + if (hasSpoiler && SpoilerEffect2.supports()) { + if (spoilerEffect2 == null) { + spoilerEffect2 = SpoilerEffect2.getInstance(container); + } + } else { + if (spoilerEffect2 != null) { + spoilerEffect2.detach(this); + spoilerEffect2 = null; + } + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (spoilerEffect2 != null) { + spoilerEffect2.detach(this); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (spoilerEffect2 != null) { + if (spoilerEffect2.destroyed) { + spoilerEffect2 = SpoilerEffect2.getInstance(this); + } else { + spoilerEffect2.attach(this); + } } } 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 927b7b04ff..1a158fbda7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java @@ -756,6 +756,8 @@ protected void onDraw(Canvas canvas) { } if (user != null) { StoriesUtilities.drawAvatarWithStory(user.id, canvas, avatarImage, avatarStoryParams); + } else if (chat != null) { + StoriesUtilities.drawAvatarWithStory(-chat.id, canvas, avatarImage, avatarStoryParams); } else { avatarImage.setImageCoords(avatarStoryParams.originalAvatarRect); avatarImage.draw(canvas); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ReactedUserHolderView.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ReactedUserHolderView.java index d64114e935..5f5c4d9058 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ReactedUserHolderView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ReactedUserHolderView.java @@ -44,6 +44,7 @@ import org.telegram.ui.Components.Premium.PremiumGradient; import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; import org.telegram.messenger.LocaleController; +import org.telegram.ui.Components.StatusBadgeComponent; import org.telegram.ui.Stories.StoriesUtilities; public class ReactedUserHolderView extends FrameLayout { @@ -59,7 +60,7 @@ public class ReactedUserHolderView extends FrameLayout { BackupImageView reactView; AvatarDrawable avatarDrawable = new AvatarDrawable(); View overlaySelectorView; - AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable rightDrawable; + StatusBadgeComponent statusBadgeComponent; public final static int ITEM_HEIGHT_DP = 50; public final static int STORY_ITEM_HEIGHT_DP = 58; Theme.ResourcesProvider resourcesProvider; @@ -128,9 +129,9 @@ public boolean setText(CharSequence value) { 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)); + statusBadgeComponent = new StatusBadgeComponent(this); titleView.setDrawablePadding(AndroidUtilities.dp(3)); - titleView.setRightDrawable(rightDrawable); + titleView.setRightDrawable(statusBadgeComponent.getDrawable()); subtitleView = new SimpleTextView(context); subtitleView.setTextSize(13); @@ -158,17 +159,8 @@ public void setUserReaction(TLRPC.User user, TLRPC.Chat chat, TLRPC.Reaction rea 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)); + int colorFilter = Theme.getColor(style == STYLE_STORY ? Theme.key_windowBackgroundWhiteBlackText : Theme.key_chats_verifiedBackground, resourcesProvider); + statusBadgeComponent.updateDrawable(user, chat, colorFilter, false); avatarDrawable.setInfo(u); if (user != null) { @@ -284,17 +276,13 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - if (rightDrawable != null) { - rightDrawable.attach(); - } + statusBadgeComponent.onAttachedToWindow(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (rightDrawable != null) { - rightDrawable.detach(); - } + statusBadgeComponent.onDetachedFromWindow(); params.onDetachFromWindow(); } 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 b9337b3338..8ca76b8cf8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell2.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell2.java @@ -27,6 +27,8 @@ import android.util.SparseArray; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; import androidx.core.content.ContextCompat; import androidx.core.graphics.ColorUtils; @@ -49,10 +51,11 @@ import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.FlickerLoadingView; import org.telegram.ui.Components.spoilers.SpoilerEffect; +import org.telegram.ui.Components.spoilers.SpoilerEffect2; import org.telegram.ui.PhotoViewer; import org.telegram.ui.Stories.recorder.DominantColors; -public class SharedPhotoVideoCell2 extends View { +public class SharedPhotoVideoCell2 extends FrameLayout { public ImageReceiver imageReceiver = new ImageReceiver(); public ImageReceiver blurImageReceiver = new ImageReceiver(); @@ -90,6 +93,7 @@ public class SharedPhotoVideoCell2 extends View { private float spoilerRevealX; private float spoilerRevealY; private float spoilerMaxRadius; + private SpoilerEffect2 mediaSpoilerEffect2; public final static int STYLE_SHARED_MEDIA = 0; public final static int STYLE_CACHE = 1; @@ -105,7 +109,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) { @@ -114,6 +118,8 @@ public SharedPhotoVideoCell2(Context context, SharedResources sharedResources, i blurImageReceiver.setImageBitmap(Utilities.stackBlurBitmapMax(imageReceiver.getBitmap())); } }); + + setWillNotDraw(false); } public void setStyle(int style) { @@ -152,6 +158,7 @@ public void setMessageObject(MessageObject messageObject, int parentColumnsCount } currentMessageObject = messageObject; isStory = currentMessageObject != null && currentMessageObject.isStory(); + updateSpoilers2(); if (messageObject == null) { imageReceiver.onDetachedFromWindow(); blurImageReceiver.onDetachedFromWindow(); @@ -385,10 +392,15 @@ protected void onDraw(Canvas canvas) { blurImageReceiver.draw(canvas); - int sColor = Color.WHITE; - mediaSpoilerEffect.setColor(ColorUtils.setAlphaComponent(sColor, (int) (Color.alpha(sColor) * 0.325f))); - mediaSpoilerEffect.setBounds((int) imageReceiver.getImageX(), (int) imageReceiver.getImageY(), (int) imageReceiver.getImageX2(), (int) imageReceiver.getImageY2()); - mediaSpoilerEffect.draw(canvas); + if (mediaSpoilerEffect2 != null) { + canvas.clipRect(imageReceiver.getImageX(), imageReceiver.getImageY(), imageReceiver.getImageX2(), imageReceiver.getImageY2()); + mediaSpoilerEffect2.draw(canvas, this, (int) imageReceiver.getImageWidth(), (int) imageReceiver.getImageHeight()); + } else { + int sColor = Color.WHITE; + mediaSpoilerEffect.setColor(ColorUtils.setAlphaComponent(sColor, (int) (Color.alpha(sColor) * 0.325f))); + mediaSpoilerEffect.setBounds((int) imageReceiver.getImageX(), (int) imageReceiver.getImageY(), (int) imageReceiver.getImageX2(), (int) imageReceiver.getImageY2()); + mediaSpoilerEffect.draw(canvas); + } canvas.restore(); invalidate(); @@ -511,6 +523,13 @@ protected void onAttachedToWindow() { imageReceiver.onAttachedToWindow(); blurImageReceiver.onAttachedToWindow(); } + if (mediaSpoilerEffect2 != null) { + if (mediaSpoilerEffect2.destroyed) { + mediaSpoilerEffect2 = SpoilerEffect2.getInstance(this); + } else { + mediaSpoilerEffect2.attach(this); + } + } } @Override @@ -524,6 +543,9 @@ protected void onDetachedFromWindow() { imageReceiver.onDetachedFromWindow(); blurImageReceiver.onDetachedFromWindow(); } + if (mediaSpoilerEffect2 != null) { + mediaSpoilerEffect2.detach(this); + } } public void setGradientView(FlickerLoadingView globalGradientView) { @@ -538,6 +560,23 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { height /= 2; } setMeasuredDimension(width, height); + updateSpoilers2(); + } + + private void updateSpoilers2() { + if (getMeasuredHeight() <= 0 || getMeasuredWidth() <= 0) { + return; + } + if (currentMessageObject != null && currentMessageObject.hasMediaSpoilers() && SpoilerEffect2.supports()) { + if (mediaSpoilerEffect2 == null) { + mediaSpoilerEffect2 = SpoilerEffect2.getInstance(this); + } + } else { + if (mediaSpoilerEffect2 != null) { + mediaSpoilerEffect2.detach(this); + mediaSpoilerEffect2 = null; + } + } } public int getMessageId() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell2.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell2.java index adebfe7db3..929e9927c1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell2.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell2.java @@ -53,6 +53,7 @@ public void setCollapseArrow(String text, boolean collapsed, Runnable onCheckCli animatedTextView.getDrawable().setAllowCancel(true); animatedTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); animatedTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + animatedTextView.setAnimationProperties(.4f, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); collapseViewContainer.addView(animatedTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT,20)); collapsedArrow = new View(getContext()); @@ -188,10 +189,22 @@ public void setEnabled(boolean value) { textView.setAlpha(1.0f); valueTextView.setAlpha(1.0f); checkBox.setAlpha(1.0f); + if (animatedTextView != null) { + animatedTextView.setAlpha(1.0f); + } + if (collapsedArrow != null) { + collapsedArrow.setAlpha(1.0f); + } } else { checkBox.setAlpha(0.5f); textView.setAlpha(0.5f); valueTextView.setAlpha(0.5f); + if (animatedTextView != null) { + animatedTextView.setAlpha(0.6f); + } + if (collapsedArrow != null) { + collapsedArrow.setAlpha(0.6f); + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/UnconfirmedAuthHintCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/UnconfirmedAuthHintCell.java new file mode 100644 index 0000000000..58325f4af3 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/UnconfirmedAuthHintCell.java @@ -0,0 +1,367 @@ +package org.telegram.ui.Cells; + +import static org.telegram.messenger.AndroidUtilities.REPLACING_TAG_TYPE_LINK; +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.RippleDrawable; +import android.os.Build; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.TextUtils; +import android.util.Log; +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; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.BotWebViewVibrationEffect; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.messenger.UnconfirmedAuthController; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.BottomSheet; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.BackupImageView; +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.CubicBezierInterpolator; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.RLottieDrawable; +import org.telegram.ui.Components.RLottieImageView; +import org.telegram.ui.Components.ScaleStateListAnimator; +import org.telegram.ui.LaunchActivity; +import org.telegram.ui.SessionsActivity; +import org.telegram.ui.Stories.recorder.ButtonWithCounterView; + +import java.util.ArrayList; + +public class UnconfirmedAuthHintCell extends FrameLayout { + + private final LinearLayout linearLayout; + private final TextView titleTextView; + private final TextView messageTextView; + + private final LinearLayout buttonsLayout; + private final TextViewWithLoading yesButton; + private final TextViewWithLoading noButton; + + public UnconfirmedAuthHintCell(Context context) { + super(context); + + linearLayout = new LinearLayout(context); + linearLayout.setOrientation(LinearLayout.VERTICAL); + + titleTextView = new TextView(context); + titleTextView.setGravity(Gravity.CENTER); + titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + titleTextView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + titleTextView.setText(LocaleController.getString(R.string.UnconfirmedAuthTitle)); + linearLayout.addView(titleTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, Gravity.TOP | Gravity.FILL_HORIZONTAL, 28, 11, 28, 0)); + + messageTextView = new TextView(context); + messageTextView.setGravity(Gravity.CENTER); + messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); + linearLayout.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, Gravity.TOP | Gravity.FILL_HORIZONTAL, 28, 5, 28, 0)); + + buttonsLayout = new LinearLayout(context); + buttonsLayout.setOrientation(LinearLayout.HORIZONTAL); + buttonsLayout.setGravity(Gravity.CENTER); + + buttonsLayout.addView(new Space(context), LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 1, Gravity.CENTER, 1)); + + yesButton = new TextViewWithLoading(context); + yesButton.setPadding(dp(10), dp(5), dp(10), dp(7)); + yesButton.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + yesButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14.22f); + yesButton.setText(LocaleController.getString(R.string.UnconfirmedAuthConfirm)); + buttonsLayout.addView(yesButton, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 30)); + + buttonsLayout.addView(new Space(context), LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 1, Gravity.CENTER, 1)); + + noButton = new TextViewWithLoading(context); + noButton.setPadding(dp(10), dp(5), dp(10), dp(7)); + noButton.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + noButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14.22f); + noButton.setText(LocaleController.getString(R.string.UnconfirmedAuthDeny)); + buttonsLayout.addView(noButton, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 30)); + + buttonsLayout.addView(new Space(context), LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 1, Gravity.CENTER, 1)); + + linearLayout.addView(buttonsLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 28, 7, 28, 8)); + + addView(linearLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + + updateColors(); + } + + public void set(final BaseFragment fragment, int currentAccount) { + final ArrayList auths = MessagesController.getInstance(currentAccount).getUnconfirmedAuthController().auths; + + titleTextView.setText(LocaleController.getString(R.string.UnconfirmedAuthTitle)); + yesButton.setText(LocaleController.getString(R.string.UnconfirmedAuthConfirm)); + yesButton.setLoading(false, false); + noButton.setText(LocaleController.getString(R.string.UnconfirmedAuthDeny)); + noButton.setLoading(false, false); + + if (auths != null && auths.size() == 1) { + String from = ""; + from += auths.get(0).device; + if (!TextUtils.isEmpty(auths.get(0).location) && !from.isEmpty()) { + from += ", "; + } + from += auths.get(0).location; + messageTextView.setText(LocaleController.formatString(R.string.UnconfirmedAuthSingle, from)); + } else if (auths != null && auths.size() > 1) { + String from = auths.get(0).location; + for (int i = 1; i < auths.size(); ++i) { + if (!TextUtils.equals(from, auths.get(i).location)) { + from = null; + break; + } + } + if (from == null) { + messageTextView.setText(LocaleController.formatPluralString("UnconfirmedAuthMultiple", auths.size())); + } else { + messageTextView.setText(LocaleController.formatPluralString("UnconfirmedAuthMultipleFrom", auths.size(), from)); + } + } + + yesButton.setOnClickListener(v -> { + SpannableStringBuilder message = AndroidUtilities.replaceSingleTag(LocaleController.getString(R.string.UnconfirmedAuthConfirmedMessage), Theme.key_undo_cancelColor, REPLACING_TAG_TYPE_LINK, () -> { + Bulletin.hideVisible(); + fragment.presentFragment(new SessionsActivity(0)); + }); + SpannableString arrowStr = new SpannableString(">"); + ColoredImageSpan span = new ColoredImageSpan(R.drawable.attach_arrow_right); + span.setOverrideColor(Theme.getColor(Theme.key_undo_cancelColor)); + span.setScale(.7f, .7f); + span.setWidth(dp(12)); + arrowStr.setSpan(span, 0, arrowStr.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + AndroidUtilities.replaceCharSequence(">", message, arrowStr); + BulletinFactory.of(fragment).createSimpleBulletin(R.raw.contact_check, LocaleController.getString(R.string.UnconfirmedAuthConfirmed), message).show(); + MessagesController.getInstance(currentAccount).getUnconfirmedAuthController().confirm(auths, success -> { + + }); + MessagesController.getInstance(currentAccount).getUnconfirmedAuthController().cleanup(); + }); + noButton.setOnClickListener(v -> { + noButton.setLoading(true); + MessagesController.getInstance(currentAccount).getUnconfirmedAuthController().deny(auths, success -> { + showLoginPreventedSheet(success); + noButton.setLoading(false); + MessagesController.getInstance(currentAccount).getUnconfirmedAuthController().cleanup(); + }); + }); + } + + public void updateColors() { + setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + titleTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + messageTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText)); + yesButton.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteValueText)); + yesButton.setBackground(Theme.createSelectorDrawable(Theme.multAlpha(Theme.getColor(Theme.key_windowBackgroundWhiteValueText), Theme.isCurrentThemeDark() ? .3f : .15f), Theme.RIPPLE_MASK_ROUNDRECT_6DP, dp(8))); + noButton.setTextColor(Theme.getColor(Theme.key_text_RedBold)); + noButton.setBackground(Theme.createSelectorDrawable(Theme.multAlpha(Theme.getColor(Theme.key_text_RedBold), Theme.isCurrentThemeDark() ? .3f : .15f), Theme.RIPPLE_MASK_ROUNDRECT_6DP, dp(8))); + } + + private static class TextViewWithLoading extends TextView { + public TextViewWithLoading(Context context) { + super(context); + } + + public void setLoading(boolean loading) { + setLoading(loading, true); + } + + public void setLoading(boolean loading, boolean animated) { + this.loading = loading; + if (!animated) { + loadingT.set(loading, true); + } + super.setPressed(isPressed() || loading); + invalidate(); + } + + @Override + public void setPressed(boolean pressed) { + super.setPressed(pressed || loading); + } + + private boolean loading; + private final AnimatedFloat loadingT = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private CircularProgressDrawable progressDrawable; + + @Override + protected void onDraw(Canvas canvas) { + float loading = loadingT.set(this.loading); + if (loading > 0) { + if (loading < 1) { + canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), (int) (0xFF * (1f - loading)), Canvas.ALL_SAVE_FLAG); + final float s = 1f - loading * .2f; + canvas.scale(s, s, getWidth() / 2f, getHeight() / 2f); + canvas.translate(0, dp(-12) * loading); + super.onDraw(canvas); + canvas.restore(); + } + + if (progressDrawable == null) { + progressDrawable = new CircularProgressDrawable(dp(16), dp(2), getCurrentTextColor()); + progressDrawable.setCallback(this); + } + progressDrawable.setColor(getCurrentTextColor()); + progressDrawable.setBounds( + getWidth() / 2, getHeight() / 2 + (int) ((1f - loading) * dp(12)), + getWidth() / 2, getHeight() / 2 + (int) ((1f - loading) * dp(12)) + ); + progressDrawable.setAlpha((int) (0xFF * loading)); + progressDrawable.draw(canvas); + invalidate(); + } else { + super.onDraw(canvas); + } + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return progressDrawable == who || super.verifyDrawable(who); + } + } + + private int height; + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int width = MeasureSpec.getSize(widthMeasureSpec); + if (width <= 0) { + width = AndroidUtilities.displaySize.x; + } + linearLayout.measure( + MeasureSpec.makeMeasureSpec(width - getPaddingLeft() - getPaddingRight(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(AndroidUtilities.displaySize.y, MeasureSpec.AT_MOST) + ); + this.height = linearLayout.getMeasuredHeight() + getPaddingTop() + getPaddingBottom() + 1; + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); + } + + public int height() { + if (getVisibility() != View.VISIBLE) { + return 0; + } + if (height <= 0) { + height = dp(72) + 1; + } + return height; + } + + public void showLoginPreventedSheet(ArrayList auths) { + if (auths == null || auths.size() == 0) { + BulletinFactory.of(Bulletin.BulletinWindow.make(getContext()), null) + .createErrorBulletin(LocaleController.getString(R.string.UnknownError)) + .show(); + return; + } + + LinearLayout linearLayout = new LinearLayout(getContext()); + linearLayout.setOrientation(LinearLayout.VERTICAL); + + RLottieImageView imageView = new RLottieImageView(getContext()); + imageView.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); + imageView.setAnimation(R.raw.ic_ban, 50, 50); + imageView.playAnimation(); + imageView.setScaleType(ImageView.ScaleType.CENTER); + imageView.setBackground(Theme.createCircleDrawable(dp(80), Theme.getColor(Theme.key_windowBackgroundWhiteValueText))); + linearLayout.addView(imageView, LayoutHelper.createLinear(80, 80, Gravity.CENTER, 0, 14, 0, 0)); + + TextView headerTextView = new TextView(getContext()); + headerTextView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + headerTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + headerTextView.setGravity(Gravity.CENTER); + headerTextView.setText(LocaleController.formatPluralString("UnconfirmedAuthDeniedTitle", auths.size())); + headerTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + linearLayout.addView(headerTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 28, 14, 28, 0)); + + TextView messageTextView = new TextView(getContext()); + messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + messageTextView.setGravity(Gravity.CENTER); + if (auths.size() == 1) { + messageTextView.setText(LocaleController.formatString(R.string.UnconfirmedAuthDeniedMessageSingle, from(auths.get(0)))); + } else { + String from = "\n"; + for (int i = 0; i < Math.min(auths.size(), 10); ++i) { + from += "• " + from(auths.get(i)) + "\n"; + } + messageTextView.setText(LocaleController.formatString(R.string.UnconfirmedAuthDeniedMessageMultiple, from)); + } + messageTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + linearLayout.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 40, 9, 40, 0)); + + FrameLayout warningLayout = new FrameLayout(getContext()); + warningLayout.setPadding(dp(10), dp(10), dp(10), dp(10)); + warningLayout.setBackground(Theme.createRoundRectDrawable(dp(8), Theme.multAlpha(Theme.getColor(Theme.key_text_RedBold), Theme.isCurrentThemeDark() ? .2f : .15f))); + + TextView warningTextView = new TextView(getContext()); + warningTextView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + warningTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + warningTextView.setGravity(Gravity.CENTER); + warningTextView.setTextColor(Theme.getColor(Theme.key_text_RedBold)); + warningTextView.setText(LocaleController.getString(R.string.UnconfirmedAuthDeniedWarning)); + warningLayout.addView(warningTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + + linearLayout.addView(warningLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 14, 19, 14, 0)); + + ButtonWithCounterView button = new ButtonWithCounterView(getContext(), null); + ScaleStateListAnimator.apply(button, 0.02f, 1.5f); + button.setText(LocaleController.getString(R.string.GotIt), false); + linearLayout.addView(button, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, 14, 20, 14, 4)); + + final BottomSheet sheet = new BottomSheet.Builder(getContext()) + .setCustomView(linearLayout) + .show(); + + sheet.setCanDismissWithSwipe(false); + sheet.setCanDismissWithTouchOutside(false); + button.setTimer(5, () -> { + sheet.setCanDismissWithSwipe(true); + sheet.setCanDismissWithTouchOutside(true); + }); + button.setOnClickListener(v -> { + if (button.isTimerActive()) { + AndroidUtilities.shakeViewSpring(button, 3); + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + } else { + sheet.dismiss(); + } + }); + } + + private static String from(UnconfirmedAuthController.UnconfirmedAuth auth) { + if (auth == null) { + return ""; + } + String from = ""; + from += auth.device; + if (!TextUtils.isEmpty(auth.location) && !from.isEmpty()) { + from += ", "; + } + from += auth.location; + return from; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChannelCreateActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChannelCreateActivity.java index 59206c4a6e..9f65db8e56 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChannelCreateActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChannelCreateActivity.java @@ -1376,7 +1376,7 @@ private void showPremiumIncreaseLimitDialog() { if (getParentActivity() == null) { return; } - LimitReachedBottomSheet limitReachedBottomSheet = new LimitReachedBottomSheet(this, getParentActivity(), LimitReachedBottomSheet.TYPE_PUBLIC_LINKS, currentAccount); + LimitReachedBottomSheet limitReachedBottomSheet = new LimitReachedBottomSheet(this, getParentActivity(), LimitReachedBottomSheet.TYPE_PUBLIC_LINKS, currentAccount, null); limitReachedBottomSheet.parentIsChannel = true; limitReachedBottomSheet.onSuccessRunnable = () -> { canCreatePublic = true; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java index e5accded9e..b1d5590c0a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java @@ -120,6 +120,7 @@ import androidx.viewpager.widget.ViewPager; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; +import com.google.android.exoplayer2.util.Consumer; import com.google.zxing.common.detector.MathUtils; import org.openintents.openpgp.OpenPgpError; @@ -2256,6 +2257,11 @@ public void onTrendingStickersShowed(boolean show) { public boolean hasForwardingMessages() { return forwardingMessages != null && !forwardingMessages.messages.isEmpty(); } + + @Override + public void onKeyboardRequested() { + checkAdjustResize(); + } } private final ChatScrollCallback chatScrollHelperCallback = new ChatScrollCallback(); @@ -3457,6 +3463,8 @@ public void onLongPress() { avatarContainer = new ChatAvatarContainer(context, this, currentEncryptedChat != null, themeDelegate); avatarContainer.allowShorterStatus = true; avatarContainer.premiumIconHiddable = true; + avatarContainer.allowDrawStories = dialog_id < 0; + avatarContainer.setClipChildren(false); AndroidUtilities.updateViewVisibilityAnimated(avatarContainer, true, 1f, false); updateTopicTitleIcon(); if (inPreviewMode || inBubbleMode) { @@ -5877,6 +5885,7 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { } } if (dy != 0) { + contentView.invalidateBlur(); hideHints(true); } if (dy != 0 && scrollingFloatingDate && !currentFloatingTopIsNotMessage) { @@ -6842,6 +6851,7 @@ protected void onLineCountChanged(int oldLineCount, int newLineCount) { } } }; + chatActivityEnterView.getEditField().adaptiveCreateLinkDialog = true; chatActivityEnterView.setDelegate(new ChatActivityEnterViewDelegate()); chatActivityEnterView.setDialogId(dialog_id, currentAccount); if (chatInfo != null) { @@ -10583,7 +10593,7 @@ private void showNoSoundHint() { if (index == -1) { return; } - noSoundHintView = new HintView(getParentActivity(), 0, themeDelegate); + noSoundHintView = new HintView(getParentActivity(), HintView.TYPE_NOSOUND, themeDelegate); noSoundHintView.setShowingDuration(10000); frameLayout.addView(noSoundHintView, index + 1, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 19, 0, 19, 0)); noSoundHintView.setAlpha(0.0f); @@ -11282,7 +11292,7 @@ private void searchLinks(final CharSequence charSequence, final boolean force) { } pendingLinkSearchString = null; foundUrls = null; - showFieldPanelForWebPage(false, foundWebPage, false); +// showFieldPanelForWebPage(false, foundWebPage, false); } final MessagesController messagesController = getMessagesController(); Utilities.searchQueue.postRunnable(() -> { @@ -11381,11 +11391,15 @@ private void searchLinks(final CharSequence charSequence, final boolean force) { } else { req.message = textToCheck.toString(); } + if (foundWebPage != null && req.message.equals(foundWebPage.displayedText)) { + return; + } linkSearchRequestId = getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { linkSearchRequestId = 0; if (error == null) { if (response instanceof TLRPC.TL_messageMediaWebPage) { foundWebPage = ((TLRPC.TL_messageMediaWebPage) response).webpage; + foundWebPage.display_url = req.message; if (foundWebPage instanceof TLRPC.TL_webPage || foundWebPage instanceof TLRPC.TL_webPagePending) { if (foundWebPage instanceof TLRPC.TL_webPagePending) { pendingLinkSearchString = req.message; @@ -12026,25 +12040,39 @@ private Runnable sendSecretMessageRead(MessageObject messageObject, boolean read if (messageObject == null || messageObject.isOut() || !messageObject.isSecretMedia() || messageObject.messageOwner.destroyTime != 0 || messageObject.messageOwner.ttl <= 0) { return null; } - messageObject.messageOwner.destroyTime = messageObject.messageOwner.ttl + getConnectionsManager().getCurrentTime(); + final boolean delete = messageObject.messageOwner.ttl != 0x7FFFFFFF; + final int ttl = messageObject.messageOwner.ttl == 0x7FFFFFFF ? 0 : messageObject.messageOwner.ttl; + messageObject.messageOwner.destroyTime = ttl + getConnectionsManager().getCurrentTime(); if (readNow) { if (currentEncryptedChat != null) { - getMessagesController().markMessageAsRead(dialog_id, messageObject.messageOwner.random_id, messageObject.messageOwner.ttl); + getMessagesController().markMessageAsRead(dialog_id, messageObject.messageOwner.random_id, ttl); } else { - getMessagesController().markMessageAsRead2(dialog_id, messageObject.getId(), null, messageObject.messageOwner.ttl, 0); + getMessagesController().markMessageAsRead2(dialog_id, messageObject.getId(), null, ttl, 0, delete); } return null; } else { return () -> { if (currentEncryptedChat != null) { - getMessagesController().markMessageAsRead(dialog_id, messageObject.messageOwner.random_id, messageObject.messageOwner.ttl); + getMessagesController().markMessageAsRead(dialog_id, messageObject.messageOwner.random_id, ttl); } else { - getMessagesController().markMessageAsRead2(dialog_id, messageObject.getId(), null, messageObject.messageOwner.ttl, 0); + getMessagesController().markMessageAsRead2(dialog_id, messageObject.getId(), null, ttl, 0, delete); } }; } } + private Runnable sendSecretMediaDelete(MessageObject messageObject) { + if (messageObject == null || messageObject.isOut() || !messageObject.isSecretMedia() || messageObject.messageOwner.ttl != 0x7FFFFFFF) { + return null; + } + final long taskId = getMessagesController().createDeleteShowOnceTask(dialog_id, messageObject.getId()); + messageObject.forceExpired = true; + ArrayList msgs = new ArrayList<>(); + msgs.add(messageObject); + updateMessages(msgs, true); + return () -> getMessagesController().doDeleteShowOnceTask(taskId, dialog_id, messageObject.getId()); + } + private void clearChatData() { messages.clear(); messagesByDays.clear(); @@ -12355,6 +12383,9 @@ public void updateMessagesVisiblePart(boolean inLayout) { MessageObject messageStarter = isTopic ? topicStarterMessageObject : threadMessageObject; + float clipTopFinal = clipTop - chatListViewPaddingVisibleOffset; + float clipBottomFinal = chatListView.getMeasuredHeight() - blurredViewBottomOffset; + for (int a = 0; a < count; a++) { View view = chatListView.getChildAt(a); MessageObject messageObject = null; @@ -12377,18 +12408,18 @@ public void updateMessagesVisiblePart(boolean inLayout) { messageCell.isBlurred = (top < clipTop && bottom > clipTop) || (bottom > chatListView.getMeasuredHeight() - blurredViewBottomOffset && top < chatListView.getMeasuredHeight() - blurredViewBottomOffset); } - if (bottom <= clipTop - chatListViewPaddingVisibleOffset || top > chatListView.getMeasuredHeight() - blurredViewBottomOffset) { + if (bottom <= clipTopFinal || top > clipBottomFinal) { if (messageCell != null) { if (blurEnabled) { - messageCell.setVisibleOnScreen(false); + messageCell.setVisibleOnScreen(false, 0, 0); } else { - messageCell.setVisibleOnScreen(true); + messageCell.setVisibleOnScreen(true, 0, 0); } } continue; } if (messageCell != null) { - messageCell.setVisibleOnScreen(true); + messageCell.setVisibleOnScreen(true, clipTopFinal - top, bottom - clipBottomFinal); } int viewTop = top >= 0 ? 0 : -top; @@ -16417,7 +16448,9 @@ public void didReceivedNotification(int id, int account, final Object... args) { obj.setIsRead(); } } - approximateHeightSum += obj.getApproximateHeight(); + if (approximateHeightSum <= AndroidUtilities.displaySize.y / 2) { + approximateHeightSum += getHeightForMessage(obj); + } if (currentUser != null) { if (currentUser.self) { obj.messageOwner.out = true; @@ -16534,7 +16567,13 @@ public void didReceivedNotification(int id, int account, final Object... args) { calendar.setTimeInMillis(((long) obj.messageOwner.date) * 1000); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); - dateMsg.date = (int) (calendar.getTimeInMillis() / 1000); + + if (chatMode == MODE_SCHEDULED && obj.messageOwner.date == 0x7ffffffe) { + dateMsg.date = 0x7ffffffe; + } else { + dateMsg.date = (int) (calendar.getTimeInMillis() / 1000); + } + MessageObject dateObj = new MessageObject(currentAccount, dateMsg, false, false); dateObj.type = MessageObject.TYPE_DATE; dateObj.contentType = 1; @@ -21279,7 +21318,20 @@ public void openAttachBotLayout(String botUsername) { } if (!attachMenuBot.inactive) { - openAttachBotLayout(user.id, attachMenuBotStartCommand); + openAttachBotLayout(user.id, attachMenuBotStartCommand, false); + } else if (attachMenuBot.show_in_attach_menu || attachMenuBot.show_in_side_menu) { + WebAppDisclaimerAlert.show(getContext(), ignore -> { + TLRPC.TL_messages_toggleBotInAttachMenu botRequest = new TLRPC.TL_messages_toggleBotInAttachMenu(); + botRequest.bot = MessagesController.getInstance(currentAccount).getInputUser(user.id); + botRequest.enabled = true; + botRequest.write_allowed = true; + ConnectionsManager.getInstance(currentAccount).sendRequest(botRequest, (response2, error2) -> AndroidUtilities.runOnUIThread(() -> { + if (error2 == null) { + MediaDataController.getInstance(currentAccount).loadAttachMenuBots(false, true); + openAttachBotLayout(user.id, attachMenuBotStartCommand, false); + } + }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); + }, null); } else { AttachBotIntroTopView introTopView = new AttachBotIntroTopView(getParentActivity()); introTopView.setColor(Theme.getColor(Theme.key_chat_attachIcon)); @@ -21299,7 +21351,7 @@ public void openAttachBotLayout(String botUsername) { if (error2 == null) { MediaDataController.getInstance(currentAccount).loadAttachMenuBots(false, true); - openAttachBotLayout(user.id, attachMenuBotStartCommand); + openAttachBotLayout(user.id, attachMenuBotStartCommand, false); } }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); }) @@ -21333,10 +21385,10 @@ public void openAttachBotLayout(String botUsername) { })); } - public void openAttachBotLayout(long botId, String startCommand) { + public void openAttachBotLayout(long botId, String startCommand, boolean justAdded) { openAttachMenu(); createChatAttachView(); - chatAttachAlert.showBotLayout(botId, startCommand); + chatAttachAlert.showBotLayout(botId, startCommand, justAdded, false); } @Override @@ -21356,7 +21408,7 @@ protected void onDialogDismiss(Dialog dialog) { @Override public boolean extendActionMode(Menu menu) { if (PhotoViewer.hasInstance() && PhotoViewer.getInstance().isVisible()) { - if (PhotoViewer.getInstance().getSelectiongLength() == 0 || menu.findItem(android.R.id.copy) == null) { + if (PhotoViewer.getInstance().getSelectionLength() == 0 || menu.findItem(android.R.id.copy) == null) { return true; } } else { @@ -24595,15 +24647,17 @@ public void setAutoDeleteHistory(int time, int action) { icons.add(R.drawable.msg_shareout); } } else if (type == 6 && !noforwards && !selectedObject.hasRevealedExtendedMedia()) { - items.add(LocaleController.getString("SaveToGallery", R.string.SaveToGallery)); - options.add(OPTION_SAVE_TO_GALLERY2); - icons.add(R.drawable.msg_gallery); - items.add(LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads)); - options.add(OPTION_SAVE_TO_DOWNLOADS_OR_MUSIC); - icons.add(R.drawable.msg_download); - items.add(LocaleController.getString("ShareFile", R.string.ShareFile)); - options.add(OPTION_SHARE); - icons.add(R.drawable.msg_shareout); + if (!selectedObject.needDrawBluredPreview()) { + items.add(LocaleController.getString("SaveToGallery", R.string.SaveToGallery)); + options.add(OPTION_SAVE_TO_GALLERY2); + icons.add(R.drawable.msg_gallery); + items.add(LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads)); + options.add(OPTION_SAVE_TO_DOWNLOADS_OR_MUSIC); + icons.add(R.drawable.msg_download); + items.add(LocaleController.getString("ShareFile", R.string.ShareFile)); + options.add(OPTION_SHARE); + icons.add(R.drawable.msg_shareout); + } } else if (type == 7) { if (selectedObject.isMask()) { items.add(LocaleController.getString("AddToMasks", R.string.AddToMasks)); @@ -24882,7 +24936,7 @@ public void setAutoDeleteHistory(int time, int action) { options.add(OPTION_VIEW_IN_TOPIC); icons.add(R.drawable.msg_viewintopic); } - if (type == 4 && !noforwards && !selectedObject.hasRevealedExtendedMedia()) { + if (type == 4 && !noforwards && !selectedObject.hasRevealedExtendedMedia() && !selectedObject.needDrawBluredPreview()) { if (selectedObject.isVideo()) { items.add(LocaleController.getString("SaveToGallery", R.string.SaveToGallery)); options.add(OPTION_SAVE_TO_GALLERY); @@ -27576,6 +27630,8 @@ public boolean onBackPressed() { } else if (chatActivityEnterView != null && chatActivityEnterView.botCommandsMenuIsShowing()) { chatActivityEnterView.hideBotCommands(); return false; + } else if (chatActivityEnterView != null && chatActivityEnterView.closeCreationLinkDialog()) { + return false; } if (backToPreviousFragment != null) { parentLayout.addFragmentToStack(backToPreviousFragment, parentLayout.getFragmentStack().size() - 1); @@ -31162,10 +31218,11 @@ public void didPressImage(ChatMessageCell cell, float x, float y) { emojiAnimationsOverlay.onTapItem(cell, ChatActivity.this, true); chatListView.cancelClickRunnables(false); } else if (message.needDrawBluredPreview()) { - Runnable action = sendSecretMessageRead(message, false); + Runnable openAction = sendSecretMessageRead(message, false); + Runnable closeAction = sendSecretMediaDelete(message); cell.invalidate(); SecretMediaViewer.getInstance().setParentActivity(getParentActivity()); - SecretMediaViewer.getInstance().openMedia(message, photoViewerProvider, action); + SecretMediaViewer.getInstance().openMedia(message, photoViewerProvider, openAction, closeAction); } else if (MessageObject.isAnimatedEmoji(message.getDocument()) && MessageObject.getInputStickerSet(message.getDocument()) != null) { ArrayList inputSets = new ArrayList<>(1); inputSets.add(MessageObject.getInputStickerSet(message.getDocument())); @@ -32735,7 +32792,7 @@ private void showChatThemeBottomSheet() { chatThemeBottomSheet = null; chatListView.setOnInterceptTouchListener(null); setChildrenEnabled(contentView, true); - ChatThemeController.clearWallpaperThumbImages(); + ChatThemeController.getInstance(currentAccount).clearWallpaperThumbImages(); }); } @@ -32768,9 +32825,10 @@ private void setChatThemeEmoticon(final String emoticon) { if (themeDelegate == null) { return; } - ChatThemeController.getInstance(currentAccount).setDialogTheme(dialog_id, emoticon, false); + ChatThemeController chatThemeController = ChatThemeController.getInstance(currentAccount); + chatThemeController.setDialogTheme(dialog_id, emoticon, false); if (!TextUtils.isEmpty(emoticon)) { - ChatThemeController.requestChatTheme(emoticon, result -> { + chatThemeController.requestChatTheme(emoticon, result -> { themeDelegate.setCurrentTheme(result, themeDelegate.wallpaper,openAnimationStartTime != 0, null); }); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatBackgroundDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatBackgroundDrawable.java index 204eee1c0e..bd0f5cadda 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatBackgroundDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatBackgroundDrawable.java @@ -24,6 +24,7 @@ import org.telegram.messenger.ImageLoader; import org.telegram.messenger.ImageLocation; import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.UserConfig; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.EmojiThemes; import org.telegram.ui.Components.BackgroundGradientDrawable; @@ -105,7 +106,7 @@ public ChatBackgroundDrawable(TLRPC.WallPaper wallPaper, boolean themeIsDark, bo wallPaper.settings.third_background_color, wallPaper.settings.fourth_background_color ); - EmojiThemes.loadWallpaperImage(wallPaper.id, wallPaper, result -> { + EmojiThemes.loadWallpaperImage(UserConfig.selectedAccount, wallPaper.id, wallPaper, result -> { motionBackgroundDrawable.setPatternBitmap(wallPaper.settings.intensity, result.second); if (parent != null) { parent.invalidate(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java index 6e9e23f03e..fb94884129 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java @@ -232,6 +232,9 @@ public void openPhotoForEdit(String file, String thumb, boolean isVideo) { @Override public boolean onDeletePhoto(int index) { + if (userId == 0) { + return true; + } TLRPC.TL_photos_updateProfilePhoto req = new TLRPC.TL_photos_updateProfilePhoto(); req.bot = getMessagesController().getInputUser(userId); req.flags |= 2; @@ -252,6 +255,7 @@ public boolean onDeletePhoto(int index) { setAvatarCell.imageView.setTranslationX(-AndroidUtilities.dp(8)); setAvatarCell.imageView.setAnimation(cameraDrawable); })); + return false; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditTypeActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditTypeActivity.java index 668652132a..3ac9ceea3e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditTypeActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditTypeActivity.java @@ -652,7 +652,7 @@ private void showPremiumIncreaseLimitDialog() { if (getParentActivity() == null) { return; } - LimitReachedBottomSheet limitReachedBottomSheet = new LimitReachedBottomSheet(this, getParentActivity(), LimitReachedBottomSheet.TYPE_PUBLIC_LINKS, currentAccount); + LimitReachedBottomSheet limitReachedBottomSheet = new LimitReachedBottomSheet(this, getParentActivity(), LimitReachedBottomSheet.TYPE_PUBLIC_LINKS, currentAccount, null); limitReachedBottomSheet.parentIsChannel = isChannel; limitReachedBottomSheet.onSuccessRunnable = () -> { canCreatePublic = true; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatReactionsEditActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatReactionsEditActivity.java index 867903c315..4808e6422d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatReactionsEditActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatReactionsEditActivity.java @@ -273,7 +273,7 @@ private void setCheckedEnableReactionCell(int selectType, boolean animated) { return; } if (enableReactionsCell != null) { - boolean checked = selectType == SELECT_TYPE_SOME; + boolean checked = selectType == SELECT_TYPE_SOME || selectType == SELECT_TYPE_ALL; enableReactionsCell.setChecked(checked); int clr = Theme.getColor(checked ? Theme.key_windowBackgroundChecked : Theme.key_windowBackgroundUnchecked); if (checked) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatRightsEditActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatRightsEditActivity.java index db1c1a9335..96417d0513 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatRightsEditActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatRightsEditActivity.java @@ -1041,7 +1041,7 @@ private void initTransfer(TLRPC.InputCheckPasswordSRP srp, TwoStepVerificationAc }), ConnectionsManager.RequestFlagWithoutLogin); } else if (error.text.equals("CHANNELS_TOO_MUCH")) { if (getParentActivity() != null && !AccountInstance.getInstance(currentAccount).getUserConfig().isPremium()) { - showDialog(new LimitReachedBottomSheet(this, getParentActivity(), LimitReachedBottomSheet.TYPE_TO0_MANY_COMMUNITIES, currentAccount)); + showDialog(new LimitReachedBottomSheet(this, getParentActivity(), LimitReachedBottomSheet.TYPE_TO0_MANY_COMMUNITIES, currentAccount, getResourceProvider())); } else { presentFragment(new TooManyCommunitiesActivity(TooManyCommunitiesActivity.TYPE_EDIT)); } 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 98f2a7265a..78ad10b841 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java @@ -17,17 +17,14 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; -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.graphics.drawable.GradientDrawable; -import android.media.AudioManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -37,7 +34,6 @@ import android.text.Html; import android.text.InputFilter; import android.text.InputType; -import android.text.Layout; import android.text.Spannable; import android.text.SpannableString; import android.text.SpannableStringBuilder; @@ -59,12 +55,10 @@ import android.widget.Button; import android.widget.EditText; import android.widget.FrameLayout; -import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; -import androidx.annotation.NonNull; import androidx.annotation.RawRes; import androidx.annotation.RequiresApi; import androidx.core.util.Consumer; @@ -113,7 +107,6 @@ import org.telegram.ui.LanguageSelectActivity; import org.telegram.ui.LaunchActivity; import org.telegram.ui.LoginActivity; -import org.telegram.ui.NotificationPermissionDialog; import org.telegram.ui.NotificationsCustomSettingsActivity; import org.telegram.ui.NotificationsSettingsActivity; import org.telegram.ui.ProfileNotificationsActivity; @@ -306,7 +299,7 @@ public static Dialog processError(int currentAccount, TLRPC.TL_error error, Base request instanceof TLRPC.TL_phone_inviteToGroupCall) { if (fragment != null && error.text.equals("CHANNELS_TOO_MUCH")) { if (fragment.getParentActivity() != null) { - fragment.showDialog(new LimitReachedBottomSheet(fragment, fragment.getParentActivity(), LimitReachedBottomSheet.TYPE_TO0_MANY_COMMUNITIES, currentAccount)); + fragment.showDialog(new LimitReachedBottomSheet(fragment, fragment.getParentActivity(), LimitReachedBottomSheet.TYPE_TO0_MANY_COMMUNITIES, currentAccount, null)); } else { if (request instanceof TLRPC.TL_channels_joinChannel || request instanceof TLRPC.TL_channels_inviteToChannel) { fragment.presentFragment(new TooManyCommunitiesActivity(TooManyCommunitiesActivity.TYPE_JOIN)); @@ -325,7 +318,7 @@ public static Dialog processError(int currentAccount, TLRPC.TL_error error, Base } else if (request instanceof TLRPC.TL_messages_createChat) { if (error.text.equals("CHANNELS_TOO_MUCH")) { if (fragment.getParentActivity() != null) { - fragment.showDialog(new LimitReachedBottomSheet(fragment, fragment.getParentActivity(), LimitReachedBottomSheet.TYPE_TO0_MANY_COMMUNITIES, currentAccount)); + fragment.showDialog(new LimitReachedBottomSheet(fragment, fragment.getParentActivity(), LimitReachedBottomSheet.TYPE_TO0_MANY_COMMUNITIES, currentAccount, null)); } else { fragment.presentFragment(new TooManyCommunitiesActivity(TooManyCommunitiesActivity.TYPE_CREATE)); } @@ -338,7 +331,7 @@ public static Dialog processError(int currentAccount, TLRPC.TL_error error, Base } else if (request instanceof TLRPC.TL_channels_createChannel) { if (error.text.equals("CHANNELS_TOO_MUCH")) { if (fragment.getParentActivity() != null) { - fragment.showDialog(new LimitReachedBottomSheet(fragment, fragment.getParentActivity(), LimitReachedBottomSheet.TYPE_TO0_MANY_COMMUNITIES, currentAccount)); + fragment.showDialog(new LimitReachedBottomSheet(fragment, fragment.getParentActivity(), LimitReachedBottomSheet.TYPE_TO0_MANY_COMMUNITIES, currentAccount, null)); } else { fragment.presentFragment(new TooManyCommunitiesActivity(TooManyCommunitiesActivity.TYPE_CREATE)); } @@ -433,7 +426,7 @@ public static Dialog processError(int currentAccount, TLRPC.TL_error error, Base showSimpleAlert(fragment, LocaleController.getString("JoinToGroupErrorFull", R.string.JoinToGroupErrorFull)); } else if (error.text.equals("CHANNELS_TOO_MUCH")) { if (fragment.getParentActivity() != null) { - fragment.showDialog(new LimitReachedBottomSheet(fragment, fragment.getParentActivity(), LimitReachedBottomSheet.TYPE_TO0_MANY_COMMUNITIES, currentAccount)); + fragment.showDialog(new LimitReachedBottomSheet(fragment, fragment.getParentActivity(), LimitReachedBottomSheet.TYPE_TO0_MANY_COMMUNITIES, currentAccount, null)); } else { fragment.presentFragment(new TooManyCommunitiesActivity(TooManyCommunitiesActivity.TYPE_JOIN)); } @@ -2513,7 +2506,7 @@ public static void checkRestrictedInviteUsers(int currentAccount, TLRPC.Chat cur AndroidUtilities.runOnUIThread(() -> { BaseFragment lastFragment = LaunchActivity.getLastFragment(); if (lastFragment != null && lastFragment.getParentActivity() != null) { - LimitReachedBottomSheet restricterdUsersBottomSheet = new LimitReachedBottomSheet(lastFragment, lastFragment.getParentActivity(), LimitReachedBottomSheet.TYPE_ADD_MEMBERS_RESTRICTED, currentAccount); + LimitReachedBottomSheet restricterdUsersBottomSheet = new LimitReachedBottomSheet(lastFragment, lastFragment.getParentActivity(), LimitReachedBottomSheet.TYPE_ADD_MEMBERS_RESTRICTED, currentAccount, null); restricterdUsersBottomSheet.setRestrictedUsers(currentChat, finalArrayList); restricterdUsersBottomSheet.show(); } 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 a59c351904..ab4b101286 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedEmojiSpan.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedEmojiSpan.java @@ -864,6 +864,10 @@ public static AnimatedEmojiSpan cloneSpan(AnimatedEmojiSpan span) { } public static CharSequence cloneSpans(CharSequence text) { + return cloneSpans(text, -1); + } + + public static CharSequence cloneSpans(CharSequence text, int newCacheType) { if (!(text instanceof Spanned)) { return text; } @@ -888,7 +892,11 @@ public static CharSequence cloneSpans(CharSequence text) { AnimatedEmojiSpan oldSpan = (AnimatedEmojiSpan) spans[i]; newText.removeSpan(oldSpan); - newText.setSpan(cloneSpan(oldSpan), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + AnimatedEmojiSpan newSpan = cloneSpan(oldSpan); + if (newCacheType != -1) { + newSpan.cacheType = newCacheType; + } + newText.setSpan(newSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } else { // newText.setSpan(spans[i], start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } 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 da084e6849..4d79077596 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedTextView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedTextView.java @@ -32,6 +32,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.checkerframework.checker.units.qual.A; import org.telegram.messenger.AndroidUtilities; import org.telegram.ui.ActionBar.Theme; @@ -265,7 +266,7 @@ public void draw(@NonNull Canvas canvas) { 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.drawRect(bounds.right - rightPadding - w, bounds.top, bounds.right - rightPadding + AndroidUtilities.dp(1), bounds.bottom, ellipsizePaint); canvas.restore(); canvas.restore(); } @@ -880,6 +881,14 @@ public void setBounds(int left, int top, int right, int bottom) { public Rect getDirtyBounds() { return this.bounds; } + + public float isNotEmpty() { + return AndroidUtilities.lerp( + oldText == null || oldText.length() <= 0 ? 0f : 1f, + currentText == null || currentText.length() <= 0 ? 0f : 1f, + oldText == null ? 1f : t + ); + } } private final AnimatedTextDrawable drawable; 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 838b372492..21f1c2b477 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurBehindDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurBehindDrawable.java @@ -70,6 +70,9 @@ public BlurBehindDrawable(View behindView, View parentView, int type, Theme.Reso } public void draw(Canvas canvas) { + if (parentView == null || parentView.getMeasuredHeight() == 0 && parentView.getMeasuredWidth() == 0) { + return; + } if (type == 1 && !wasDraw && !animateAlpha) { generateBlurredBitmaps(); invalidate = false; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurringShader.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurringShader.java new file mode 100644 index 0000000000..cc48da38ae --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurringShader.java @@ -0,0 +1,1013 @@ +package org.telegram.ui.Components; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.animation.ValueAnimator; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +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.RectF; +import android.graphics.Shader; +import android.graphics.drawable.Drawable; +import android.opengl.GLES11Ext; +import android.opengl.GLES20; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.util.ArrayList; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLContext; + +public class BlurringShader { + + private FilterGLThread parent; + + public BlurringShader() { + this(null); + } + + public BlurringShader(FilterGLThread parent) { + this.parent = parent; + } + + public void invalidate() { + if (this.parent != null) { + this.parent.requestRender(false); + } + } + + private int width = 1, height = 1; + private int padding = 0; + + private Program[] program = new Program[2]; + + private FloatBuffer posBuffer; + private FloatBuffer padPosBuffer; + private FloatBuffer uvBuffer; + + private static class Program { + int gl; + + int posHandle; + int uvHandle; + int matrixHandle; + int texHandle; + int szHandle; + int texSzHandle; + int gradientTopHandle; + int gradientBottomHandle; + int stepHandle; + int flipyHandle; + int videoMatrixHandle; + int hasVideoMatrixHandle; + + public Program(int gl) { + this.gl = gl; + posHandle = GLES20.glGetAttribLocation(gl, "p"); + uvHandle = GLES20.glGetAttribLocation(gl, "inputuv"); + matrixHandle = GLES20.glGetUniformLocation(gl, "matrix"); + texHandle = GLES20.glGetUniformLocation(gl, "tex"); + szHandle = GLES20.glGetUniformLocation(gl, "sz"); + texSzHandle = GLES20.glGetUniformLocation(gl, "texSz"); + gradientTopHandle = GLES20.glGetUniformLocation(gl, "gtop"); + gradientBottomHandle = GLES20.glGetUniformLocation(gl, "gbottom"); + stepHandle = GLES20.glGetUniformLocation(gl, "step"); + videoMatrixHandle = GLES20.glGetUniformLocation(gl, "videoMatrix"); + hasVideoMatrixHandle = GLES20.glGetUniformLocation(gl, "hasVideoMatrix"); + flipyHandle = GLES20.glGetUniformLocation(gl, "flipy"); + } + } + + private boolean setupTransform; + private final float[] m3x3 = new float[9]; + private final float[] matrix = new float[16]; + private final Object matrixLock = new Object(); + + private int gradientTop, gradientBottom; + + private final Object bitmapLock = new Object(); + private ByteBuffer buffer; + private Bitmap bitmap; + private boolean bitmapAvailable; + + private final int[] framebuffer = new int[3]; + private final int[] texture = new int[3]; + + public boolean setup(float aspectRatio, boolean needsUiBitmap, int padding) { + + final float scale = 1.5f; + final float density = 9 * scale * 16 * scale; + width = (int) Math.round(Math.sqrt(density * aspectRatio)); + height = (int) Math.round(Math.sqrt(density / aspectRatio)); + this.padding = padding; + + if (!setupTransform) { + updateTransform(new Matrix(), 1, 1); + } + + float[] posCoords = { + -1.f, 1.f, + 1.f, 1.f, + -1.f, -1.f, + 1.f, -1.f + }; + + ByteBuffer bb = ByteBuffer.allocateDirect(posCoords.length * 4); + bb.order(ByteOrder.nativeOrder()); + posBuffer = bb.asFloatBuffer(); + posBuffer.put(posCoords); + posBuffer.position(0); + + for (int i = 0; i < 4; ++i) { + posCoords[2 * i] *= (width - padding) / (float) width; + posCoords[2 * i + 1] *= (height - padding) / (float) height; + } + + bb = ByteBuffer.allocateDirect(posCoords.length * 4); + bb.order(ByteOrder.nativeOrder()); + padPosBuffer = bb.asFloatBuffer(); + padPosBuffer.put(posCoords); + padPosBuffer.position(0); + + float[] texCoords = { + 0.f, 1.f, + 1.f, 1.f, + 0.f, 0.f, + 1.f, 0.f + }; + + bb = ByteBuffer.allocateDirect(texCoords.length * 4); + bb.order(ByteOrder.nativeOrder()); + uvBuffer = bb.asFloatBuffer(); + uvBuffer.put(texCoords); + uvBuffer.position(0); + + String vertexShaderSource = RLottieDrawable.readRes(null, R.raw.blur_vrt); + String fragmentShaderSource = RLottieDrawable.readRes(null, R.raw.blur_frg); + if (vertexShaderSource == null || fragmentShaderSource == null) { + return false; + } + + for (int a = 0; a < 2; ++a) { + if (a == 1) { + fragmentShaderSource = "#extension GL_OES_EGL_image_external : require\n" + fragmentShaderSource.replace("sampler2D tex", "samplerExternalOES tex"); + } + final int vertexShader = FilterShaders.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderSource); + final int fragmentShader = FilterShaders.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderSource); + if (vertexShader == 0 || fragmentShader == 0) { + return false; + } + final int program = GLES20.glCreateProgram(); + GLES20.glAttachShader(program, vertexShader); + GLES20.glAttachShader(program, fragmentShader); + GLES20.glBindAttribLocation(program, 0, "p"); + GLES20.glBindAttribLocation(program, 1, "inputuv"); + GLES20.glLinkProgram(program); + int[] linkStatus = new int[1]; + GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); + if (linkStatus[0] == 0) { + GLES20.glDeleteProgram(program); + return false; + } + this.program[a] = new Program(program); + } + + GLES20.glGenFramebuffers(3, framebuffer, 0); + GLES20.glGenTextures(3, texture, 0); + + for (int a = 0; a < 3; ++a) { + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture[a]); + final int w = width + (a == 2 ? 2 * padding : 0); + final int h = height + (a == 2 ? 2 * padding : 0); + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, w, h, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebuffer[a]); + GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, texture[a], 0); + + int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER); + if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) { + return false; + } + } + + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + + if (needsUiBitmap) { + bitmap = Bitmap.createBitmap(2 * padding + width, 2 * padding + height, Bitmap.Config.ARGB_8888); + buffer = ByteBuffer.allocateDirect((2 * padding + width) * (2 * padding + height) * 4); + } + + return true; + } + + public void draw(float[] oesMatrix, int texture, int textureWidth, int textureHeight) { + final boolean oes = oesMatrix != null; + Program p = program[oes ? 1 : 0]; + if (p == null) { + return; + } + + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebuffer[0]); + GLES20.glViewport(0, 0, width, height); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + + GLES20.glUseProgram(p.gl); + + GLES20.glUniform1i(p.texHandle, 0); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + if (oes) { + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture); + } else { + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture); + } + + GLES20.glEnableVertexAttribArray(p.uvHandle); + GLES20.glVertexAttribPointer(p.uvHandle, 2, GLES20.GL_FLOAT, false, 8, uvBuffer); + GLES20.glEnableVertexAttribArray(p.posHandle); + GLES20.glVertexAttribPointer(p.posHandle, 2, GLES20.GL_FLOAT, false, 8, posBuffer); + GLES20.glUniform2f(p.szHandle, width, height); + GLES20.glUniform2f(p.texSzHandle, textureWidth, textureHeight); + GLES20.glUniform1i(p.stepHandle, 0); + GLES20.glUniform1f(p.flipyHandle, oes ? 1f : 0f); + if (oes) { + GLES20.glUniformMatrix4fv(p.videoMatrixHandle, 1, false, oesMatrix, 0); + } + GLES20.glUniform1f(p.hasVideoMatrixHandle, oes ? 1 : 0); + + org.telegram.ui.Components.Paint.Shader.SetColorUniform(p.gradientTopHandle, gradientTop); + org.telegram.ui.Components.Paint.Shader.SetColorUniform(p.gradientBottomHandle, gradientBottom); + + synchronized (matrixLock) { + GLES20.glUniformMatrix4fv(p.matrixHandle, 1, false, this.matrix, 0); + } + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + + if (oes) { + p = program[0]; + if (p == null) { + return; + } + GLES20.glUseProgram(p.gl); + + GLES20.glEnableVertexAttribArray(p.uvHandle); + GLES20.glVertexAttribPointer(p.uvHandle, 2, GLES20.GL_FLOAT, false, 8, uvBuffer); + GLES20.glEnableVertexAttribArray(p.posHandle); + GLES20.glVertexAttribPointer(p.posHandle, 2, GLES20.GL_FLOAT, false, 8, posBuffer); + GLES20.glUniform2f(p.szHandle, width, height); + GLES20.glUniform2f(p.texSzHandle, textureWidth, textureHeight); + GLES20.glUniform1i(p.stepHandle, 0); + + org.telegram.ui.Components.Paint.Shader.SetColorUniform(p.gradientTopHandle, gradientTop); + org.telegram.ui.Components.Paint.Shader.SetColorUniform(p.gradientBottomHandle, gradientBottom); + + GLES20.glUniform1f(p.flipyHandle, 0f); + + synchronized (matrixLock) { + GLES20.glUniformMatrix4fv(p.matrixHandle, 1, false, this.matrix, 0); + } + } + + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebuffer[1]); + GLES20.glUniform1i(p.stepHandle, 1); + + GLES20.glUniform1i(p.texHandle, 0); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, this.texture[0]); + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebuffer[2]); + GLES20.glViewport(0, 0, width + 2 * padding, height + 2 * padding); + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + + GLES20.glEnableVertexAttribArray(p.posHandle); + GLES20.glVertexAttribPointer(p.posHandle, 2, GLES20.GL_FLOAT, false, 8, padPosBuffer); + GLES20.glUniform1i(p.stepHandle, 2); + + GLES20.glUniform1i(p.texHandle, 0); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, this.texture[1]); + + Object lock = null; + if (currentManager != null) { + lock = currentManager.getTextureLock(); + } + if (lock != null) { + synchronized (lock) { + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + } + } else { + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + } + + if (buffer != null) { + buffer.rewind(); + GLES20.glReadPixels(0, 0, width + 2 * padding, height + 2 * padding, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buffer); + synchronized (bitmapLock) { + bitmap.copyPixelsFromBuffer(buffer); + bitmapAvailable = true; + } + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + } + + AndroidUtilities.cancelRunOnUIThread(this.invalidateViews); + AndroidUtilities.runOnUIThread(this.invalidateViews); + } + + public int getTexture() { + return texture[2]; + } + + public Bitmap getBitmap() { + synchronized (bitmapLock) { + if (!bitmapAvailable) { + return null; + } + return bitmap; + } + } + + public void resetBitmap() { + synchronized (bitmapLock) { + bitmapAvailable = false; + } + } + + public void updateGradient(int top, int bottom) { + gradientTop = top; + gradientBottom = bottom; + } + + private BlurManager currentManager; + public void setBlurManager(BlurManager manager) { + if (currentManager != null) { + currentManager.setShader(null); + } + this.currentManager = manager; + if (currentManager != null) { + currentManager.setShader(this); + } + } + + private final Runnable invalidateViews = () -> { + if (currentManager != null) { + currentManager.invalidate(); + } + }; + + private final Matrix iMatrix = new Matrix(); + public void updateTransform(Matrix matrix, int w, int h) { + matrix.invert(iMatrix); + iMatrix.preScale(w, h); + iMatrix.postScale(1f / w, 1f / h); + updateTransform(iMatrix); + } + + public void updateTransform(Matrix matrix) { + setupTransform = true; + matrix.getValues(this.m3x3); + synchronized (matrixLock) { + this.matrix[0] = m3x3[0]; + this.matrix[1] = m3x3[3]; + this.matrix[2] = 0; + this.matrix[3] = m3x3[6]; + + this.matrix[4] = m3x3[1]; + this.matrix[5] = m3x3[4]; + this.matrix[6] = 0; + this.matrix[7] = m3x3[7]; + + this.matrix[8] = 0; + this.matrix[9] = 0; + this.matrix[10] = 1; + this.matrix[11] = 0; + + this.matrix[12] = m3x3[2]; + this.matrix[13] = m3x3[5]; + this.matrix[14] = 0; + this.matrix[15] = m3x3[8]; + } + } + + public static class BlurManager { + + public int padding; + private final View view; + private final ArrayList parents = new ArrayList<>(); + private final ArrayList holders = new ArrayList<>(); + private final ArrayList invalidateHolders = new ArrayList<>(); + + private final Object contextLock = new Object(); + private EGLContext context; + + private final Object textureLock = new Object(); + + public BlurManager(View parentView) { + this.view = parentView; + if (view.isAttachedToWindow()) { + updateParents(); + } + view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(@NonNull View v) { + updateParents(); + } + @Override + public void onViewDetachedFromWindow(@NonNull View v) { + parents.clear(); + } + }); + } + + public EGLContext getParentContext() { + synchronized (contextLock) { + if (context != null) { + return context; + } + return EGL10.EGL_NO_CONTEXT; + } + } + + public void acquiredContext(EGLContext newContext) { + synchronized (contextLock) { + if (context == null) { + context = newContext; + } + } + } + + public void destroyedContext(EGLContext oldContext) { + synchronized (contextLock) { + if (context == oldContext) { + context = null; + } + } + } + + public Object getTextureLock() { + return textureLock; + } + + public int getTexture() { + if (currentShader != null) { + return currentShader.getTexture(); + } + return -1; + } + + private void updateParents() { + parents.clear(); + View view = this.view; + while (view != null) { + parents.add(0, view); + if (!(view.getParent() instanceof View)) { + break; + } + view = (View) view.getParent(); + } + } + + private BlurringShader currentShader; + public void setShader(BlurringShader shader) { + if (currentShader == shader) { + return; + } + currentShader = shader; + if (currentShader != null) { + invalidate(); + } + } + + public void invalidate() { + for (StoryBlurDrawer holder : holders) { + holder.view.invalidate(); + } + for (Runnable invalidate : invalidateHolders) { + invalidate.run(); + } + } + + public void attach(StoryBlurDrawer holder) { + holders.add(holder); + } + + public void detach(StoryBlurDrawer holder) { + holders.remove(holder); + if (invalidateHolders.isEmpty() && holders.isEmpty()) { + thumbBlurer.destroy(); + } + } + + public void attach(Runnable invalidate) { + invalidateHolders.add(invalidate); + } + + public void detach(Runnable invalidate) { + invalidateHolders.remove(invalidate); + if (invalidateHolders.isEmpty() && holders.isEmpty()) { + thumbBlurer.destroy(); + } + } + + public Bitmap getBitmap() { + if (currentShader == null) { + return fallbackBitmap; + } + Bitmap blurBitmap = currentShader.getBitmap(); + return blurBitmap == null ? fallbackBitmap : blurBitmap; + } + + private void invalidateFallbackBlur() { + fallbackBitmap = thumbBlurer.thumbBitmap; + invalidate(); + } + + private final ThumbBlurer thumbBlurer = new ThumbBlurer(0, this::invalidateFallbackBlur); + private Bitmap fallbackBitmap; + + private int i = 0; + public void setFallbackBlur(Bitmap bitmap, int orientation) { + fallbackBitmap = thumbBlurer.getBitmap(bitmap, "" + i++, orientation, 0); + } + + public void resetBitmap() { + if (currentShader != null) { + currentShader.resetBitmap(); + } + } + } + + public static class ThumbBlurer { + + private String thumbKey; + private Bitmap thumbBitmap; + + private final Paint clearPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final int padding; + private final Runnable invalidate; + + private Runnable generate; + + public ThumbBlurer() { + this(1); + } + + public ThumbBlurer(int padding) { + this.padding = padding; + this.invalidate = null; + clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + } + + public ThumbBlurer(int padding, Runnable invalidate) { + this.padding = padding; + this.invalidate = invalidate; + clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + } + + public void destroy() { + thumbKey = null; + if (generate != null) { + Utilities.globalQueue.cancelRunnable(generate); + } + if (thumbBitmap != null && !thumbBitmap.isRecycled()) { + thumbBitmap.recycle(); + } + thumbBitmap = null; + } + + public Bitmap getBitmap(Bitmap bitmap, String key, int orientation, int invert) { + if (bitmap == null) { + return null; + } + if (TextUtils.equals(thumbKey, key)) { + if (thumbBitmap != null) { + return thumbBitmap; + } else if (generate != null) { + return null; + } + } + if (generate != null) { + Utilities.globalQueue.cancelRunnable(generate); + } + thumbKey = key; + Utilities.globalQueue.postRunnable(generate = () -> { + final float aspectRatio = bitmap.getWidth() / (float) bitmap.getHeight(); + final float scale = 1.5f; + final float density = 9 * scale * 16 * scale; + int width = (int) Math.round(Math.sqrt(density * aspectRatio)); + int height = (int) Math.round(Math.sqrt(density / aspectRatio)); + int fwidth, fheight; + if (orientation == 90 || orientation == 270) { + fwidth = height; + fheight = width; + } else { + fwidth = width; + fheight = height; + } + Bitmap resultBitmap = Bitmap.createBitmap(2 * padding + fwidth, 2 * padding + fheight, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(resultBitmap); + final Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); + final Rect dst = new Rect(padding, padding, padding + width, padding + height); + canvas.translate(padding + fwidth / 2f, padding + fheight / 2f); + if (invert == 1) { + canvas.scale(-1, 1); + } else if (invert == 2) { + canvas.scale(1, -1); + } + canvas.rotate(orientation); + canvas.translate(-padding - width / 2f, -padding - height / 2f); + canvas.drawBitmap(bitmap, src, dst, null); + Utilities.stackBlurBitmap(resultBitmap, 6); + if (padding > 0) { + // clear borders + canvas.drawRect(0, 0, width + padding, padding, clearPaint); // top + canvas.drawRect(0, padding, padding, padding + height, clearPaint); + canvas.drawRect(padding + width, padding, padding + width + padding, padding + height, clearPaint); + canvas.drawRect(0, padding + height, padding + width + padding, padding + height + padding, clearPaint); + } + AndroidUtilities.runOnUIThread(() -> { + if (TextUtils.equals(thumbKey, key)) { + generate = null; + if (thumbBitmap != null) { + thumbBitmap.recycle(); + } + thumbBitmap = resultBitmap; + if (invalidate != null) { + invalidate.run(); + } + } else { + resultBitmap.recycle(); + } + }); + }); + return thumbBitmap; + } + + public Bitmap getBitmap(ImageReceiver imageReceiver) { + if (imageReceiver == null) { + return null; + } + return getBitmap(imageReceiver.getBitmap(), imageReceiver.getImageKey(), imageReceiver.getOrientation(), imageReceiver.getInvert()); + } + + public Bitmap getBitmap(ImageReceiver.BitmapHolder bitmapHolder) { + if (bitmapHolder == null) { + return null; + } + return getBitmap(bitmapHolder.bitmap, bitmapHolder.getKey(), bitmapHolder.orientation, 0); + } + } + + public static class StoryBlurDrawer { + + public static final int BLUR_TYPE_BACKGROUND = 0; + public static final int BLUR_TYPE_CAPTION = 1; + public static final int BLUR_TYPE_CAPTION_XFER = 2; + public static final int BLUR_TYPE_AUDIO_BACKGROUND = 3; + public static final int BLUR_TYPE_AUDIO_WAVEFORM_BACKGROUND = 4; + public static final int BLUR_TYPE_MENU_BACKGROUND = 5; + public static final int BLUR_TYPE_SHADOW = 6; + public static final int BLUR_TYPE_EMOJI_VIEW = 7; + + private final BlurManager manager; + private final View view; + + private boolean animateBitmapChange; + private boolean oldPaintSet; + private float oldPaintAlpha; + public Paint oldPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + + public Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + private final int type; + + public StoryBlurDrawer(@Nullable BlurManager manager, @NonNull View view, int type) { + this(manager, view, type, false); + } + + public StoryBlurDrawer(@Nullable BlurManager manager, @NonNull View view, int type, boolean animateBitmapChange) { + this.manager = manager; + this.view = view; + this.type = type; + this.animateBitmapChange = animateBitmapChange; + + final ColorMatrix colorMatrix = new ColorMatrix(); + if (type == BLUR_TYPE_BACKGROUND) { + AndroidUtilities.adjustSaturationColorMatrix(colorMatrix, +.45f); + } else if (type == BLUR_TYPE_MENU_BACKGROUND) { + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); + oldPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); + AndroidUtilities.adjustSaturationColorMatrix(colorMatrix, +.3f); + } else if (type == BLUR_TYPE_CAPTION_XFER) { + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); + oldPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); + AndroidUtilities.adjustBrightnessColorMatrix(colorMatrix, +.8f); + AndroidUtilities.adjustSaturationColorMatrix(colorMatrix, +.45f); + AndroidUtilities.multiplyBrightnessColorMatrix(colorMatrix, 2.5f); + AndroidUtilities.adjustBrightnessColorMatrix(colorMatrix, +.8f); + } else if (type == BLUR_TYPE_CAPTION) { + AndroidUtilities.adjustSaturationColorMatrix(colorMatrix, +.35f); + AndroidUtilities.adjustBrightnessColorMatrix(colorMatrix, +.7f); + AndroidUtilities.multiplyBrightnessColorMatrix(colorMatrix, 1.5f); + } else if (type == BLUR_TYPE_AUDIO_BACKGROUND) { + AndroidUtilities.adjustSaturationColorMatrix(colorMatrix, +.5f); + } else if (type == BLUR_TYPE_AUDIO_WAVEFORM_BACKGROUND) { + AndroidUtilities.adjustSaturationColorMatrix(colorMatrix, +.6f); + AndroidUtilities.adjustBrightnessColorMatrix(colorMatrix, +.3f); + AndroidUtilities.multiplyBrightnessColorMatrix(colorMatrix, 1.2f); + } else if (type == BLUR_TYPE_SHADOW) { + AndroidUtilities.adjustSaturationColorMatrix(colorMatrix, +.4f); + AndroidUtilities.multiplyBrightnessColorMatrix(colorMatrix, 0.35f); + } else if (type == BLUR_TYPE_EMOJI_VIEW) { + AndroidUtilities.adjustSaturationColorMatrix(colorMatrix, +.5f); + AndroidUtilities.multiplyBrightnessColorMatrix(colorMatrix, .85f); + } + paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); + oldPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); + + if (this.view.isAttachedToWindow() && manager != null) { + manager.attach(this); + } + this.view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(@NonNull View v) { + if (manager != null) { + manager.attach(StoryBlurDrawer.this); + } + } + + @Override + public void onViewDetachedFromWindow(@NonNull View v) { + if (manager != null) { + manager.detach(StoryBlurDrawer.this); + } + recycle(); + } + }); + } + + private boolean customOffset; + private float customOffsetX, customOffsetY; + public StoryBlurDrawer setOffset(float ox, float oy) { + customOffset = true; + customOffsetX = ox; + customOffsetY = oy; + return this; + } + + private Bitmap lastBitmap; + private BitmapShader bitmapShader; + private final Matrix matrix = new Matrix(); + RectF bounds = new RectF(); + + private void updateBounds() { + Bitmap bitmap = manager.getBitmap(); + if (bitmap == null) { + return; + } + if (bitmapShader == null || lastBitmap != bitmap) { + bitmapShader = new BitmapShader(lastBitmap = bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + paint.setShader(bitmapShader); + } + float sx = bounds.width() / (float) lastBitmap.getWidth(); + float sy = bounds.height() / (float) lastBitmap.getHeight(); + + matrix.reset(); + matrix.postTranslate(bounds.left, bounds.top); + matrix.preScale(sx, sy); + + bitmapShader.setLocalMatrix(matrix); + } + + public void setBounds(float left, float top, float right, float bottom) { + AndroidUtilities.rectTmp.set(left, top, right, bottom); + setBounds(AndroidUtilities.rectTmp); + } + + 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(); + } + + @Nullable + public Paint getPaint(float alpha) { + return getPaint(alpha, 0, 0); + } + + @Nullable + public Paint getPaint(float alpha, float tx, float ty) { + if (manager == null) { + return null; + } + Bitmap bitmap = manager.getBitmap(); + if (bitmap == null) { + return null; + } + + if (bitmapShader == null || lastBitmap != bitmap) { + if (animateBitmapChange && bitmapShader != null && lastBitmap != null && !lastBitmap.isRecycled() && !bitmap.isRecycled()) { + Paint tempPaint = paint; + paint = oldPaint; + oldPaint = tempPaint; + oldPaintSet = true; + animateOldPaint(); + } + bitmapShader = new BitmapShader(lastBitmap = bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + paint.setShader(bitmapShader); + } + + if (!setupMatrix(bitmap.getWidth(), bitmap.getHeight())) { + return null; + } + matrix.postTranslate(-tx, -ty); + bitmapShader.setLocalMatrix(matrix); + paint.setAlpha((int) (0xFF * alpha)); + + return paint; + } + + private Paint[] tempPaints; + public Paint[] getPaints(float alpha, float tx, float ty) { + Paint paint2 = getPaint(alpha, tx, ty); + Paint paint1 = oldPaintSet ? oldPaint : null; + if (paint2 != null && oldPaintSet) { + paint2.setAlpha((int) (0xFF * (1f - oldPaintAlpha) * alpha)); + } + if (paint1 != null) { + paint1.setAlpha((int) (0xFF * alpha)); + } + if (tempPaints == null) { + tempPaints = new Paint[2]; + } + tempPaints[0] = paint1; + tempPaints[1] = paint2; + return tempPaints; + } + + private ValueAnimator crossfadeAnimator; + private void animateOldPaint() { + if (crossfadeAnimator != null) { + crossfadeAnimator.cancel(); + crossfadeAnimator = null; + } + + oldPaintAlpha = 1f; + crossfadeAnimator = ValueAnimator.ofFloat(oldPaintAlpha, 0f); + crossfadeAnimator.addUpdateListener(anm -> { + oldPaintAlpha = (float) anm.getAnimatedValue(); + this.view.invalidate(); + }); + crossfadeAnimator.start(); + } + + private void recycle() { + lastBitmap = null; + paint.setShader(bitmapShader = null); + } + + private boolean setupMatrix(int bitmapWidth, int bitmapHeight) { + matrix.reset(); + if (customOffset) { + matrix.postTranslate(-customOffsetX, -customOffsetY); + } else { + View view = this.view; + do { + matrix.preScale(1f / view.getScaleX(), 1f / view.getScaleY(), view.getPivotX(), view.getPivotY()); + matrix.preTranslate(-view.getX(), -view.getY()); + if (view.getParent() instanceof View) { + view = (View) view.getParent(); + } else { + break; + } + } while (view != null && manager != null && !manager.parents.contains(view)); + + if (manager != null && manager.view != view) { + int index = manager.parents.indexOf(view) + 1; + while (index >= 0 && index < manager.parents.size()) { + View child = manager.parents.get(index); + if (child == null) { + continue; + } + matrix.postTranslate(child.getX(), child.getY()); + matrix.postScale(1f / child.getScaleX(), 1f / child.getScaleY(), child.getPivotX(), child.getPivotY()); + index++; + } + } + } + + if (manager != null && manager.view != null) { + matrix.preScale((float) manager.view.getWidth() / bitmapWidth, (float) manager.view.getHeight() / bitmapHeight); + } + return true; + } + + public Drawable makeDrawable(float offsetX, float offsetY, Drawable base) { + return new Drawable() { + + float alpha = 1f; + private final Paint dimPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + { + dimPaint.setColor(0x52000000); + } + + @Nullable + private Paint getPaint() { + if (manager == null) { + return null; + } + Bitmap bitmap = manager.getBitmap(); + if (bitmap == null) { + return null; + } + + if (bitmapShader == null || lastBitmap != bitmap) { + bitmapShader = new BitmapShader(lastBitmap = bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + paint.setShader(bitmapShader); + } + + matrix.reset(); + matrix.postTranslate(-customOffsetX - offsetX, -customOffsetY - offsetY); + if (manager.view != null) { + matrix.preScale((float) manager.view.getWidth() / bitmap.getWidth(), (float) manager.view.getHeight() / bitmap.getHeight()); + } + bitmapShader.setLocalMatrix(matrix); + paint.setAlpha((int) (0xFF * alpha)); + return paint; + } + + @Override + public void draw(@NonNull Canvas canvas) { + Paint paint = getPaint(); + + Rect bounds = getBounds(); + if (paint != null) { + canvas.saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom, 0xFF, Canvas.ALL_SAVE_FLAG); + base.setBounds(bounds); + base.draw(canvas); + canvas.drawRect(bounds, paint); + canvas.restore(); + getPadding(AndroidUtilities.rectTmp2); + AndroidUtilities.rectTmp.set( + bounds.left + AndroidUtilities.rectTmp2.left, + bounds.top + AndroidUtilities.rectTmp2.top, + bounds.right - AndroidUtilities.rectTmp2.right, + bounds.bottom - AndroidUtilities.rectTmp2.bottom + ); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(6), dp(6), dimPaint); + } else { + base.setBounds(bounds); + base.draw(canvas); + } + } + + @Override + public void setAlpha(int alpha) { + this.alpha = alpha / (float) 0xFF; + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) {} + + @Override + public int getOpacity() { + return PixelFormat.TRANSPARENT; + } + + @Override + public boolean getPadding(@NonNull Rect padding) { + return base.getPadding(padding); + } + }; + } + } +} 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 605ad68c7b..0541f0a492 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BotWebViewContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BotWebViewContainer.java @@ -88,7 +88,7 @@ import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; -public class BotWebViewContainer extends FrameLayout implements NotificationCenter.NotificationCenterDelegate { +public abstract class BotWebViewContainer extends FrameLayout implements NotificationCenter.NotificationCenterDelegate { private final static String DURGER_KING_USERNAME = "DurgerKingBot"; private final static int REQUEST_CODE_WEB_VIEW_FILE = 3000, REQUEST_CODE_WEB_PERMISSION = 4000, REQUEST_CODE_QR_CAMERA_PERMISSION = 5000; private final static int DIALOG_SEQUENTIAL_COOLDOWN_TIME = 3000; @@ -512,6 +512,8 @@ public void onPermissionRequestCanceled(PermissionRequest request) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { webView.addJavascriptInterface(new WebViewProxy(), "TelegramWebviewProxy"); } + + onWebViewCreated(); } private void onOpenUri(Uri uri) { @@ -863,27 +865,29 @@ private void setupFlickerParams(boolean center) { } public void reload() { - checkCreateWebView(); - - isPageLoaded = false; - lastClickMs = 0; - hasUserPermissions = false; - if (webView != null) { - webView.reload(); - } + NotificationCenter.getInstance(currentAccount).doOnIdle(() -> { + checkCreateWebView(); + isPageLoaded = false; + lastClickMs = 0; + hasUserPermissions = false; + if (webView != null) { + webView.reload(); + } + }); } public void loadUrl(int currentAccount, String url) { - checkCreateWebView(); - this.currentAccount = currentAccount; - isPageLoaded = false; - lastClickMs = 0; - hasUserPermissions = false; - mUrl = url; - if (webView != null) { - webView.loadUrl(url); - } + NotificationCenter.getInstance(currentAccount).doOnIdle(() -> { + isPageLoaded = false; + lastClickMs = 0; + hasUserPermissions = false; + mUrl = url; + checkCreateWebView(); + if (webView != null) { + webView.loadUrl(url); + } + }); } @Override @@ -937,22 +941,25 @@ public void evaluateJs(String script) { @SuppressWarnings("deprecation") public void evaluateJs(String script, boolean create) { - if (create) { - checkCreateWebView(); - } - if (webView == null) { - return; - } + NotificationCenter.getInstance(currentAccount).doOnIdle(() -> { + if (create) { + checkCreateWebView(); + } + if (webView == null) { + return; + } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - webView.evaluateJavascript(script, value -> {}); - } else { - try { - webView.loadUrl("javascript:" + URLEncoder.encode(script, "UTF-8")); - } catch (UnsupportedEncodingException e) { - webView.loadUrl("javascript:" + URLEncoder.encode(script)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + webView.evaluateJavascript(script, value -> { + }); + } else { + try { + webView.loadUrl("javascript:" + URLEncoder.encode(script, "UTF-8")); + } catch (UnsupportedEncodingException e) { + webView.loadUrl("javascript:" + URLEncoder.encode(script)); + } } - } + }); } @Override @@ -1442,6 +1449,9 @@ public void didReceivedNotification(int id, int account, Object... args) { ConnectionsManager.getInstance(currentAccount).sendRequest(req2, (res2, err2) -> AndroidUtilities.runOnUIThread(() -> { if (res2 != null) { status[0] = "allowed"; + if (res2 instanceof TLRPC.Updates) { + MessagesController.getInstance(currentAccount).processUpdates((TLRPC.Updates) res2, false); + } } if (err2 != null) { unknownError(err2.text); @@ -1707,6 +1717,10 @@ private String hexFixed(int h) { return hex; } + public void onWebViewCreated() { + + } + private class WebViewProxy { @JavascriptInterface public void postEvent(String eventType, String eventData) { 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 fb9009158c..a17af97097 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BotWebViewMenuContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BotWebViewMenuContainer.java @@ -145,7 +145,12 @@ public BotWebViewMenuContainer(@NonNull Context context, ChatActivityEnterView p ActionBar actionBar = chatActivity.getActionBar(); actionBarOnItemClick = actionBar.getActionBarMenuOnItemClick(); - webViewContainer = new BotWebViewContainer(context, parentEnterView.getParentFragment().getResourceProvider(), getColor(Theme.key_windowBackgroundWhite)); + webViewContainer = new BotWebViewContainer(context, parentEnterView.getParentFragment().getResourceProvider(), getColor(Theme.key_windowBackgroundWhite)) { + @Override + public void onWebViewCreated() { + swipeContainer.setWebView(webViewContainer.getWebView()); + } + }; webViewContainer.setDelegate(webViewDelegate = new BotWebViewContainer.Delegate() { @Override @@ -813,7 +818,12 @@ public void onDismiss() { webViewContainer.destroyWebView(); swipeContainer.removeView(webViewContainer); - webViewContainer = new BotWebViewContainer(getContext(), parentEnterView.getParentFragment().getResourceProvider(), getColor(Theme.key_windowBackgroundWhite)); + webViewContainer = new BotWebViewContainer(getContext(), parentEnterView.getParentFragment().getResourceProvider(), getColor(Theme.key_windowBackgroundWhite)) { + @Override + public void onWebViewCreated() { + swipeContainer.setWebView(webViewContainer.getWebView()); + } + }; webViewContainer.setDelegate(webViewDelegate); webViewContainer.setWebViewProgressListener(progress -> { progressView.setLoadProgressAnimated(progress); 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 765f6204ae..53389b53e3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BotWebViewSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BotWebViewSheet.java @@ -1,5 +1,7 @@ package org.telegram.ui.Components; +import static org.telegram.ui.Components.Bulletin.DURATION_PROLONG; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; @@ -30,15 +32,18 @@ import androidx.core.content.ContextCompat; import androidx.core.graphics.ColorUtils; import androidx.core.math.MathUtils; +import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; import org.json.JSONObject; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ContactsController; 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.NotificationCenter; import org.telegram.messenger.R; @@ -69,6 +74,35 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi public final static int TYPE_WEB_VIEW_BUTTON = 0, TYPE_SIMPLE_WEB_VIEW_BUTTON = 1, TYPE_BOT_MENU_BUTTON = 2, TYPE_WEB_VIEW_BOT_APP = 3; public final static int FLAG_FROM_INLINE_SWITCH = 1; + public final static int FLAG_FROM_SIDE_MENU = 2; + + public void showJustAddedBulletin() { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(botId); + TLRPC.TL_attachMenuBot currentBot = null; + for (TLRPC.TL_attachMenuBot bot : MediaDataController.getInstance(currentAccount).getAttachMenuBots().bots) { + if (bot.bot_id == botId) { + currentBot = bot; + break; + } + } + if (currentBot == null) { + return; + } + String str; + if (currentBot.show_in_side_menu && currentBot.show_in_attach_menu) { + str = LocaleController.formatString("BotAttachMenuShortcatAddedAttachAndSide", R.string.BotAttachMenuShortcatAddedAttachAndSide, user.first_name); + } else if (currentBot.show_in_side_menu) { + str = LocaleController.formatString("BotAttachMenuShortcatAddedSide", R.string.BotAttachMenuShortcatAddedSide, user.first_name); + } else { + str = LocaleController.formatString("BotAttachMenuShortcatAddedAttach", R.string.BotAttachMenuShortcatAddedAttach, user.first_name); + } + AndroidUtilities.runOnUIThread(() -> { + BulletinFactory.of(frameLayout, resourcesProvider) + .createSimpleBulletin(R.raw.contact_check, AndroidUtilities.replaceTags(str)) + .setDuration(DURATION_PROLONG) + .show(true); + }, 200); + } @Retention(RetentionPolicy.SOURCE) @IntDef(value = { @@ -204,7 +238,13 @@ public void requestLayout() { super.requestLayout(); } }; - webViewContainer = new BotWebViewContainer(context, resourcesProvider, getColor(Theme.key_windowBackgroundWhite)); + webViewContainer = new BotWebViewContainer(context, resourcesProvider, getColor(Theme.key_windowBackgroundWhite)) { + @Override + public void onWebViewCreated() { + super.onWebViewCreated(); + swipeContainer.setWebView(webViewContainer.getWebView()); + } + }; webViewContainer.setDelegate(new BotWebViewContainer.Delegate() { private boolean sentWebViewData; @@ -465,6 +505,23 @@ public boolean onTouchEvent(MotionEvent event) { } return super.onTouchEvent(event); } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + Bulletin.addDelegate(this, new Bulletin.Delegate() { + @Override + public int getTopOffset(int tag) { + return AndroidUtilities.statusBarHeight; + } + }); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + Bulletin.removeDelegate(this); + } }; frameLayout.setDelegate((keyboardHeight, isWidthGreater) -> { if (keyboardHeight > AndroidUtilities.dp(20)) { @@ -755,9 +812,21 @@ public void requestWebView(int currentAccount, long peerId, long botId, String b ActionBarMenu menu = actionBar.createMenu(); menu.removeAllViews(); + TLRPC.TL_attachMenuBot currentBot = null; + for (TLRPC.TL_attachMenuBot bot : MediaDataController.getInstance(currentAccount).getAttachMenuBots().bots) { + if (bot.bot_id == botId) { + currentBot = bot; + break; + } + } + ActionBarMenuItem otherItem = menu.addItem(0, R.drawable.ic_ab_other); otherItem.addSubItem(R.id.menu_open_bot, R.drawable.msg_bot, LocaleController.getString(R.string.BotWebViewOpenBot)); + settingsItem = otherItem.addSubItem(R.id.menu_settings, R.drawable.msg_settings, LocaleController.getString(R.string.BotWebViewSettings)); otherItem.addSubItem(R.id.menu_reload_page, R.drawable.msg_retry, LocaleController.getString(R.string.BotWebViewReloadPage)); + if (currentBot != null && (currentBot.show_in_side_menu || currentBot.show_in_attach_menu)) { + otherItem.addSubItem(R.id.menu_delete_bot, R.drawable.msg_delete, LocaleController.getString(R.string.BotWebViewDeleteBot)); + } actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override public void onItemClick(int id) { @@ -787,6 +856,8 @@ public void onItemClick(int id) { webViewContainer.reload(); } else if (id == R.id.menu_settings) { webViewContainer.onSettingsButtonPressed(); + } else if (id == R.id.menu_delete_bot) { + deleteBot(currentAccount, botId, () -> dismiss()); } } }); @@ -831,8 +902,6 @@ public void onItemClick(int id) { TLRPC.TL_webViewResultUrl resultUrl = (TLRPC.TL_webViewResultUrl) response; queryId = resultUrl.query_id; webViewContainer.loadUrl(currentAccount, resultUrl.url); - swipeContainer.setWebView(webViewContainer.getWebView()); - AndroidUtilities.runOnUIThread(pollRunnable, POLL_PERIOD); } })); @@ -845,6 +914,7 @@ public void onItemClick(int id) { req.from_switch_webview = (flags & FLAG_FROM_INLINE_SWITCH) != 0; req.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); req.platform = "android"; + req.from_side_menu = (flags & FLAG_FROM_SIDE_MENU) != 0;; if (hasThemeParams) { req.theme_params = new TLRPC.TL_dataJSON(); req.theme_params.data = themeParams; @@ -857,7 +927,6 @@ public void onItemClick(int id) { TLRPC.TL_simpleWebViewResultUrl resultUrl = (TLRPC.TL_simpleWebViewResultUrl) response; queryId = 0; webViewContainer.loadUrl(currentAccount, resultUrl.url); - swipeContainer.setWebView(webViewContainer.getWebView()); } })); break; @@ -888,8 +957,6 @@ public void onItemClick(int id) { TLRPC.TL_webViewResultUrl resultUrl = (TLRPC.TL_webViewResultUrl) response; queryId = resultUrl.query_id; webViewContainer.loadUrl(currentAccount, resultUrl.url); - swipeContainer.setWebView(webViewContainer.getWebView()); - AndroidUtilities.runOnUIThread(pollRunnable, POLL_PERIOD); } })); @@ -924,7 +991,6 @@ public void onItemClick(int id) { TLRPC.TL_appWebViewResultUrl result = (TLRPC.TL_appWebViewResultUrl) response2; queryId = 0; webViewContainer.loadUrl(currentAccount, result.url); - swipeContainer.setWebView(webViewContainer.getWebView()); AndroidUtilities.runOnUIThread(pollRunnable, POLL_PERIOD); } @@ -933,6 +999,41 @@ public void onItemClick(int id) { } } + public static void deleteBot(int currentAccount, long botId, Runnable onDone) { + String description; + TLRPC.TL_attachMenuBot currentBot = null; + for (TLRPC.TL_attachMenuBot bot : MediaDataController.getInstance(currentAccount).getAttachMenuBots().bots) { + if (bot.bot_id == botId) { + currentBot = bot; + break; + } + } + if (currentBot == null) { + return; + } + String botName = currentBot.short_name; + description = LocaleController.formatString("BotRemoveFromMenu", R.string.BotRemoveFromMenu, botName); + TLRPC.TL_attachMenuBot finalCurrentBot = currentBot; + new AlertDialog.Builder(LaunchActivity.getLastFragment().getContext()) + .setTitle(LocaleController.getString(R.string.BotRemoveFromMenuTitle)) + .setMessage(AndroidUtilities.replaceTags(description)) + .setPositiveButton(LocaleController.getString("OK", R.string.OK), (dialogInterface, i) -> { + TLRPC.TL_messages_toggleBotInAttachMenu req = new TLRPC.TL_messages_toggleBotInAttachMenu(); + req.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); + req.enabled = false; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + MediaDataController.getInstance(currentAccount).loadAttachMenuBots(false, true); + }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); + finalCurrentBot.show_in_side_menu = false; + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.attachMenuBotsDidLoad); + if (onDone != null) { + onDone.run(); + } + }) + .setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null) + .show(); + } + private int getColor(int key) { return Theme.getColor(key, resourcesProvider); } @@ -948,11 +1049,15 @@ public void onLayoutChange(View v, int left, int top, int right, int bottom, int swipeContainer.setSwipeOffsetY(swipeContainer.getHeight()); frameLayout.setAlpha(1f); + AnimationNotificationsLocker locker = new AnimationNotificationsLocker(); + locker.lock(); new SpringAnimation(swipeContainer, ChatAttachAlertBotWebViewLayout.WebViewSwipeContainer.SWIPE_OFFSET_Y, 0) .setSpring(new SpringForce(0) .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY) .setStiffness(500.0f) - ).start(); + ).addEndListener((animation, canceled, value, velocity) -> { + locker.unlock(); + }).start(); } }); super.show(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BottomPagerTabs.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BottomPagerTabs.java new file mode 100644 index 0000000000..7b61e26b36 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BottomPagerTabs.java @@ -0,0 +1,287 @@ +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.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.view.MotionEvent; +import android.view.View; + +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; + +public class BottomPagerTabs extends View { + + private final Theme.ResourcesProvider resourcesProvider; + private final Tab[] tabs; + + protected 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(BottomPagerTabs.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(BottomPagerTabs.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 BottomPagerTabs(Context context, Theme.ResourcesProvider resourcesProvider) { + super(context); + this.resourcesProvider = resourcesProvider; + + tabs = createTabs(); + + setPadding(dp(12), 0, dp(12), 0); + + setProgress(0, false); + } + + public Tab[] createTabs() { + return new Tab[0]; + } + + 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); + } +} \ No newline at end of file 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 ac8f8e30bf..be9a92c4b6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BulletinFactory.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BulletinFactory.java @@ -306,18 +306,18 @@ public Bulletin createUndoBulletin(CharSequence text, Runnable onUndo, Runnable return create(layout, Bulletin.DURATION_PROLONG); } - public Bulletin createUsersBulletin(List users, CharSequence text) { + public Bulletin createUsersBulletin(List users, CharSequence text) { return createUsersBulletin(users, text, null, null); } - public Bulletin createUsersBulletin(List users, CharSequence text, CharSequence subtitle, UndoObject undoObject) { + 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) { for (int i = 0; i < users.size(); ++i) { if (count >= 3) break; - TLRPC.User user = users.get(i); + TLObject user = users.get(i); if (user != null) { layout.avatarsImageView.setCount(++count); layout.avatarsImageView.setObject(count - 1, UserConfig.selectedAccount, user); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/CaptionPhotoViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/CaptionPhotoViewer.java new file mode 100644 index 0000000000..630edd7126 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/CaptionPhotoViewer.java @@ -0,0 +1,308 @@ +package org.telegram.ui.Components; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.ui.ActionBar.Theme.RIPPLE_MASK_CIRCLE_20DP; + +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.RectF; +import android.view.Gravity; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import com.google.android.exoplayer2.util.Util; + +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.Utilities; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Stories.DarkThemeResourceProvider; +import org.telegram.ui.Stories.recorder.CaptionContainerView; +import org.telegram.ui.Stories.recorder.HintView2; + +public class CaptionPhotoViewer extends CaptionContainerView { + + private boolean addPhotoVisible; + private final ImageView addPhotoButton; + + private boolean timerVisible; + private final ImageView timerButton; + private final PeriodDrawable timerDrawable; + private ItemOptions timerPopup; + + private int timer = 0; + private final int SHOW_ONCE = 0x7FFFFFFF; + private final int[] values = new int[] { SHOW_ONCE, 3, 10, 30, 0 }; + +// private final BlurringShader.StoryBlurDrawer hintBlur; + private final HintView2 hint; + private final Runnable applyCaption; + + public CaptionPhotoViewer(Context context, FrameLayout rootView, SizeNotifierFrameLayout sizeNotifierFrameLayout, FrameLayout containerView, Theme.ResourcesProvider resourcesProvider, BlurringShader.BlurManager blurManager, Runnable applyCaption) { + super(context, rootView, sizeNotifierFrameLayout, containerView, resourcesProvider, blurManager); + this.applyCaption = applyCaption; + + addPhotoButton = new ImageView(context); + addPhotoButton.setImageResource(R.drawable.filled_add_photo); + addPhotoButton.setScaleType(ImageView.ScaleType.CENTER); + addPhotoButton.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); + addPhotoButton.setBackground(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR, RIPPLE_MASK_CIRCLE_20DP, dp(18))); + setAddPhotoVisible(false, false); + addView(addPhotoButton, LayoutHelper.createFrame(44, 44, Gravity.LEFT | Gravity.BOTTOM, 14, 0, 0, 10)); + + timerButton = new ImageView(context); + timerButton.setImageDrawable(timerDrawable = new PeriodDrawable()); + timerButton.setBackground(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR, RIPPLE_MASK_CIRCLE_20DP, dp(18))); + timerButton.setScaleType(ImageView.ScaleType.CENTER); + setTimerVisible(false, false); + addView(timerButton, LayoutHelper.createFrame(44, 44, Gravity.RIGHT | Gravity.BOTTOM, 0, 0, 11, 10)); + + hint = new HintView2(context, HintView2.DIRECTION_BOTTOM);// { +// @Override +// protected boolean drawBlur(Canvas canvas, RectF bounds, Path path, float alpha) { +// if (customBlur()) { +// canvas.saveLayerAlpha(bounds, (int) (0xFF * alpha), Canvas.ALL_SAVE_FLAG); +// canvas.clipPath(path); +// CaptionPhotoViewer.this.drawBlur(hintBlur, canvas, bounds, 0, false, 0, -hint.getY(), false); +// canvas.restore(); +// return true; +// } +// return false; +// } +// }; +// hintBlur = new BlurringShader.StoryBlurDrawer(blurManager, hint, BlurringShader.StoryBlurDrawer.BLUR_TYPE_MENU_BACKGROUND); + hint.setRounding(12); + hint.setPadding(dp(12), 0, dp(12), dp(8)); + hint.setJoint(1, -21); + hint.setMultilineText(true); + addView(hint, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 80, Gravity.RIGHT | Gravity.BOTTOM)); + + timerButton.setOnClickListener(e -> { + if (timerPopup != null && timerPopup.isShown()) { + timerPopup.dismiss(); + timerPopup = null; + return; + } + hint.hide(); + + timerPopup = ItemOptions.makeOptions(rootView, new DarkThemeResourceProvider(), timerButton); + timerPopup.setDimAlpha(0); + timerPopup.addText(LocaleController.getString(R.string.TimerPeriodHint), 13); + timerPopup.addGap(); + for (int value : values) { + String text; + if (value == 0) { + text = LocaleController.getString(R.string.TimerPeriodDoNotDelete); + } else if (value == SHOW_ONCE) { + text = LocaleController.getString(R.string.TimerPeriodOnce); + } else { + text = LocaleController.formatPluralString("Seconds", value); + } + timerPopup.add(0, text, () -> changeTimer(value)); + if (this.timer == value) { + timerPopup.putCheck(); + } + } + timerPopup.show(); + }); + } + + public void setOnAddPhotoClick(View.OnClickListener listener) { + addPhotoButton.setOnClickListener(listener); + } + + public void setAddPhotoVisible(boolean visible, boolean animated) { + addPhotoVisible = visible; + addPhotoButton.animate().cancel(); + if (animated) { + addPhotoButton.setVisibility(View.VISIBLE); + addPhotoButton.animate().alpha(visible ? 1f : 0f).translationX(visible ? 0 : dp(-8)).withEndAction(() -> { + if (!visible) { + timerButton.setVisibility(View.GONE); + } + }).start(); + } else { + addPhotoButton.setVisibility(visible ? View.VISIBLE : View.GONE); + addPhotoButton.setAlpha(visible ? 1f : 0f); + addPhotoButton.setTranslationX(visible ? 0 : dp(-8)); + } + updateEditTextLeft(); + + MarginLayoutParams lp = (MarginLayoutParams) editText.getLayoutParams(); + lp.rightMargin = dp(12 + (addPhotoVisible && timerVisible ? 33 : 0)); + editText.setLayoutParams(lp); + } + + @Override + protected int getEditTextLeft() { + return addPhotoVisible ? dp(31) : 0; + } + + private boolean isVideo; + public void setIsVideo(boolean isVideo) { + this.isVideo = isVideo; + } + + @Override + protected void onTextChange() { + if (applyCaption != null) { + applyCaption.run(); + } + } + + public void setTimerVisible(boolean visible, boolean animated) { + timerVisible = visible; + timerButton.animate().cancel(); + if (animated) { + timerButton.setVisibility(View.VISIBLE); + timerButton.animate().alpha(visible ? 1f : 0f).translationX(visible ? 0 : dp(8)).withEndAction(() -> { + if (!visible) { + timerButton.setVisibility(View.GONE); + } + }).start(); + } else { + timerButton.setVisibility(visible ? View.VISIBLE : View.GONE); + timerButton.setAlpha(visible ? 1f : 0f); + timerButton.setTranslationX(visible ? 0 : dp(8)); + } + + MarginLayoutParams lp = (MarginLayoutParams) editText.getLayoutParams(); + lp.rightMargin = dp(12 + (addPhotoVisible && timerVisible ? 33 : 0)); + editText.setLayoutParams(lp); + } + + public boolean hasTimer() { + return timerVisible && timer > 0; + } + + public void setTimer(int value) { + this.timer = value; + timerDrawable.setValue(timer == SHOW_ONCE ? 1 : Math.max(1, timer), timer > 0, true); + if (hint != null) { + hint.hide(); + } + } + + private void changeTimer(int value) { + if (this.timer == value) { + return; + } + setTimer(value); + if (onTTLChange != null) { + onTTLChange.run(value); + } + CharSequence text; + if (value == 0) { + text = LocaleController.getString(isVideo ? R.string.TimerPeriodVideoKeep : R.string.TimerPeriodPhotoKeep); + hint.setMaxWidthPx(getMeasuredWidth()); + hint.setMultilineText(false); + hint.setInnerPadding(13, 4, 10, 4); + hint.setIconMargin(0); + hint.setIconTranslate(0, -dp(1)); + } else if (value == SHOW_ONCE) { + text = LocaleController.getString(isVideo ? R.string.TimerPeriodVideoSetOnce : R.string.TimerPeriodPhotoSetOnce); + hint.setMaxWidthPx(getMeasuredWidth()); + hint.setMultilineText(false); + hint.setInnerPadding(13, 4, 10, 4); + hint.setIconMargin(0); + hint.setIconTranslate(0, -dp(1)); + } else if (value > 0) { + text = AndroidUtilities.replaceTags(LocaleController.formatPluralString(isVideo ? "TimerPeriodVideoSetSeconds" : "TimerPeriodPhotoSetSeconds", value)); + hint.setMultilineText(true); + hint.setMaxWidthPx(HintView2.cutInFancyHalf(text, hint.getTextPaint())); + hint.setInnerPadding(12, 7, 11, 7); + hint.setIconMargin(2); + hint.setIconTranslate(0, 0); + } else { + return; + } + hint.setTranslationY(-Math.min(dp(34), getEditTextHeight()) - dp(14)); + hint.setText(text); + final int iconResId = value > 0 ? R.raw.fire_on : R.raw.fire_off; + RLottieDrawable icon = new RLottieDrawable(iconResId, "" + iconResId, dp(34), dp(34)); + icon.start(); + hint.setIcon(icon); + hint.show(); + } + + @Override + protected void onEditHeightChange(int height) { + hint.setTranslationY(-Math.min(dp(34), height) - dp(10)); + } + + @Override + protected boolean clipChild(View child) { + return child != hint; + } + + private Utilities.Callback onTTLChange; + public void setOnTimerChange(Utilities.Callback onTTLChange) { + this.onTTLChange = onTTLChange; + } + + @Override + protected int getCaptionLimit() { + return UserConfig.getInstance(currentAccount).isPremium() ? getCaptionPremiumLimit() : getCaptionDefaultLimit(); + } + + @Override + protected int getCaptionDefaultLimit() { + return MessagesController.getInstance(currentAccount).captionLengthLimitDefault; + } + + @Override + protected int getCaptionPremiumLimit() { + return MessagesController.getInstance(currentAccount).captionLengthLimitPremium; + } + + @Override + protected void beforeUpdateShownKeyboard(boolean show) { + if (!show) { + timerButton.setVisibility(timerVisible ? View.VISIBLE : View.GONE); + addPhotoButton.setVisibility(addPhotoVisible ? View.VISIBLE : View.GONE); + } + if (hint != null) { + hint.hide(); + } + } + + @Override + protected void onUpdateShowKeyboard(float keyboardT) { + timerButton.setAlpha(1f - keyboardT); + addPhotoButton.setAlpha(1f - keyboardT); + } + + @Override + protected void afterUpdateShownKeyboard(boolean show) { + timerButton.setVisibility(!show && timerVisible ? View.VISIBLE : View.GONE); + addPhotoButton.setVisibility(!show && addPhotoVisible ? View.VISIBLE : View.GONE); + if (show) { + timerButton.setVisibility(View.GONE); + addPhotoButton.setVisibility(View.GONE); + } + } + + @Override + protected int additionalKeyboardHeight() { + return 0; + } + + @Override + public void updateColors(Theme.ResourcesProvider resourcesProvider) { + super.updateColors(resourcesProvider); + timerDrawable.updateColors(0xffffffff, Theme.getColor(Theme.key_dialogFloatingButton, resourcesProvider)); + } + + @Override + protected void setupMentionContainer() { + + } +} 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 ff833ea8dc..121df4b7ea 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java @@ -205,6 +205,7 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific private ActionBarMenuSubItem sendWhenOnlineButton; private LinearLayout recordTimeContainer; private CharSequence overrideHint; + private CharSequence overrideHint2; float emojiButtonScale = 1f; float emojiButtonAlpha = 1f; float emojiButtonPaddingScale = 1f; @@ -355,6 +356,10 @@ default TLRPC.TL_channels_sendAsPeers getSendAsPeers() { default TLRPC.StoryItem getReplyToStory() { return null; } + + default void onKeyboardRequested() { + + } } public final static int RECORD_STATE_ENTER = 0; @@ -677,6 +682,9 @@ public void run() { } if (!destroyed && messageEditText != null && waitingForKeyboardOpen && !keyboardVisible && !AndroidUtilities.usingHardwareInput && !AndroidUtilities.isInMultiwindow) { + if (delegate != null) { + delegate.onKeyboardRequested(); + } messageEditText.requestFocus(); AndroidUtilities.showKeyboard(messageEditText); AndroidUtilities.cancelRunOnUIThread(openKeyboardRunnable); @@ -5731,6 +5739,9 @@ public void onResume() { int visibility = getVisibility(); if (showKeyboardOnResume && parentFragment != null && parentFragment.isLastFragment()) { showKeyboardOnResume = false; + if (delegate != null) { + delegate.onKeyboardRequested(); + } if (searchingType == 0 && messageEditText != null) { messageEditText.requestFocus(); } @@ -5875,6 +5886,7 @@ public void updateFieldHint(boolean animated) { } if (overrideHint != null) { messageEditText.setHintText(overrideHint, animated); + messageEditText.setHintText2(overrideHint2, animated); return; } if (!sendPlainEnabled && !isEditingMessage()) { @@ -10353,6 +10365,9 @@ private void openKeyboardInternal() { return; } showPopup(AndroidUtilities.usingHardwareInput || AndroidUtilities.isInMultiwindow || parentFragment != null && parentFragment.isInBubbleMode() || isPaused ? 0 : 2, 0); + if (delegate != null) { + delegate.onKeyboardRequested(); + } if (messageEditText != null) { messageEditText.requestFocus(); } @@ -10389,6 +10404,9 @@ public void openKeyboard() { if (hasBotWebView() && botCommandsMenuIsShowing()) { return; } + if (delegate != null) { + delegate.onKeyboardRequested(); + } if (messageEditText != null && !AndroidUtilities.showKeyboard(messageEditText)) { messageEditText.clearFocus(); messageEditText.requestFocus(); @@ -10403,6 +10421,10 @@ public boolean isPopupShowing() { return emojiViewVisible || botKeyboardViewVisible; } + public boolean closeCreationLinkDialog() { + return messageEditText != null && messageEditText.closeCreationLinkDialog(); + } + public boolean isKeyboardVisible() { return keyboardVisible; } @@ -10718,7 +10740,7 @@ public void didReceivedNotification(int id, int account, Object... args) { TLRPC.DocumentAttribute attribute = audioToSend.attributes.get(a); if (attribute instanceof TLRPC.TL_documentAttributeAudio) { if (attribute.waveform == null || attribute.waveform.length == 0) { - attribute.waveform = MediaController.getInstance().getWaveform(audioToSendPath); + attribute.waveform = MediaController.getWaveform(audioToSendPath); } recordedAudioSeekBar.setWaveform(attribute.waveform); break; @@ -11820,6 +11842,14 @@ public void setHorizontalPadding(float padding, float progress, boolean allowSha recordedAudioBackground.invalidate(); } } + if (messageEditText != null) { + float scale = AndroidUtilities.lerp(0.88f, 1f, progress); + messageEditText.setPivotX(0); + messageEditText.setPivotY(messageEditText.getMeasuredHeight() / 2f); + messageEditText.setScaleX(scale); + messageEditText.setScaleY(scale); + messageEditText.setHintRightOffset(AndroidUtilities.lerp(AndroidUtilities.dp(30), 0, progress)); + } } private void updateMessageTextParams() { @@ -11848,6 +11878,13 @@ public void setOverrideHint(CharSequence overrideHint) { public void setOverrideHint(CharSequence overrideHint, boolean animated) { this.overrideHint = overrideHint; + this.overrideHint2 = null; + updateFieldHint(animated); + } + + public void setOverrideHint(CharSequence overrideHint, CharSequence overrideHint2, boolean animated) { + this.overrideHint = overrideHint; + this.overrideHint2 = overrideHint2; updateFieldHint(animated); } 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 a0bfd17a64..72ef53f152 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java @@ -72,6 +72,7 @@ import org.jetbrains.annotations.NotNull; 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; @@ -143,6 +144,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N private boolean isSoundPicker = false; private boolean isEmojiPicker = false; public boolean isStoryLocationPicker = false; + public boolean isStoryAudioPicker = false; private ImageUpdater.AvatarFor setAvatarFor; public boolean pinnedToTop; @@ -164,11 +166,11 @@ public float getClipLayoutBottom() { return frameLayout2.getMeasuredHeight() - alphaOffset; } - public void showBotLayout(long id) { - showBotLayout(id, null); + public void showBotLayout(long id, boolean animated) { + showBotLayout(id, null, false, animated); } - public void showBotLayout(long id, String startCommand) { + public void showBotLayout(long id, String startCommand, boolean justAdded, boolean animated) { if (botAttachLayouts.get(id) == null || !Objects.equals(startCommand, botAttachLayouts.get(id).getStartCommand()) || botAttachLayouts.get(id).needReload()) { if (baseFragment instanceof ChatActivity) { ChatAttachAlertBotWebViewLayout webViewLayout = new ChatAttachAlertBotWebViewLayout(this, getContext(), resourcesProvider); @@ -378,7 +380,11 @@ public boolean isClipboardAvailable() { } if (botAttachLayouts.get(id) != null) { botAttachLayouts.get(id).disallowSwipeOffsetAnimation(); - showLayout(botAttachLayouts.get(id), -id); + showLayout(botAttachLayouts.get(id), -id, animated); + + if (justAdded) { + botAttachLayouts.get(id).showJustAddedBulletin(); + } } } @@ -1815,14 +1821,14 @@ protected void onAttachedToWindow() { adjustPanLayoutHelper.setResizableView(this); adjustPanLayoutHelper.onAttach(); commentTextView.setAdjustPanLayoutHelper(adjustPanLayoutHelper); - // Bulletin.addDelegate(this, bulletinDelegate); + // Bulletin.addDelegate(this, bulletinDelegate); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); adjustPanLayoutHelper.onDetach(); - // Bulletin.removeDelegate(this); + // Bulletin.removeDelegate(this); } }; sizeNotifierFrameLayout.setDelegate(new SizeNotifierFrameLayout.SizeNotifierFrameLayoutDelegate() { @@ -2134,7 +2140,7 @@ public void setTranslationY(float translationY) { } else if (num == 4) { if (Build.VERSION.SDK_INT >= 33) { if (activity.checkSelfPermission(Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED || - activity.checkSelfPermission(Manifest.permission.READ_MEDIA_VIDEO) != PackageManager.PERMISSION_GRANTED) { + activity.checkSelfPermission(Manifest.permission.READ_MEDIA_VIDEO) != PackageManager.PERMISSION_GRANTED) { activity.requestPermissions(new String[]{Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO}, BasePermissionsActivity.REQUEST_CODE_EXTERNAL_STORAGE); return; } @@ -2190,7 +2196,7 @@ public void setTranslationY(float translationY) { } else if (view instanceof AttachBotButton) { AttachBotButton button = (AttachBotButton) view; if (button.attachMenuBot != null) { - showBotLayout(button.attachMenuBot.bot_id); + showBotLayout(button.attachMenuBot.bot_id, true); } else { delegate.didSelectBot(button.currentUser); dismiss(); @@ -2530,7 +2536,8 @@ public void getOutline(View view, Outline outline) { AndroidUtilities.shakeView(captionLimitView); try { captionLimitView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); - } catch (Exception ignored) {} + } catch (Exception ignored) { + } if (!MessagesController.getInstance(currentAccount).premiumLocked && MessagesController.getInstance(currentAccount).captionLengthLimitPremium > codepointCount) { showCaptionLimitBulletin(parentFragment); @@ -2772,7 +2779,7 @@ private void showCaptionLimitBulletin(BaseFragment parentFragment) { return; } - BulletinFactory.of(sizeNotifierFrameLayout, resourcesProvider).createCaptionLimitBulletin(MessagesController.getInstance(currentAccount).captionLengthLimitPremium, ()->{ + BulletinFactory.of(sizeNotifierFrameLayout, resourcesProvider).createCaptionLimitBulletin(MessagesController.getInstance(currentAccount).captionLengthLimitPremium, () -> { dismiss(true); if (parentFragment != null) { parentFragment.presentFragment(new PremiumPreviewFragment("caption_limit")); @@ -2795,9 +2802,18 @@ private boolean isLightStatusBar() { public void onLongClickBotButton(TLRPC.TL_attachMenuBot attachMenuBot, TLRPC.User currentUser) { String botName = attachMenuBot != null ? attachMenuBot.short_name : UserObject.getUserName(currentUser); + String description; + TLRPC.TL_attachMenuBot currentBot = null; + for (TLRPC.TL_attachMenuBot bot : MediaDataController.getInstance(currentAccount).getAttachMenuBots().bots) { + if (bot.bot_id == currentUser.id) { + currentBot = bot; + break; + } + } + description = LocaleController.formatString("BotRemoveFromMenu", R.string.BotRemoveFromMenu, botName); new AlertDialog.Builder(getContext()) .setTitle(LocaleController.getString(R.string.BotRemoveFromMenuTitle)) - .setMessage(AndroidUtilities.replaceTags(attachMenuBot != null ? LocaleController.formatString("BotRemoveFromMenu", R.string.BotRemoveFromMenu, botName) : LocaleController.formatString("BotRemoveInlineFromMenu", R.string.BotRemoveInlineFromMenu, botName))) + .setMessage(AndroidUtilities.replaceTags(attachMenuBot != null ? description : LocaleController.formatString("BotRemoveInlineFromMenu", R.string.BotRemoveInlineFromMenu, botName))) .setPositiveButton(LocaleController.getString("OK", R.string.OK), (dialogInterface, i) -> { if (attachMenuBot != null) { TLRPC.TL_messages_toggleBotInAttachMenu req = new TLRPC.TL_messages_toggleBotInAttachMenu(); @@ -2958,6 +2974,10 @@ public void showLayout(AttachAlertLayout layout) { } private void showLayout(AttachAlertLayout layout, long newId) { + showLayout(layout, newId, true); + } + + private void showLayout(AttachAlertLayout layout, long newId, boolean animated) { if (viewChangeAnimator != null || commentsAnimator != null) { return; } @@ -3034,41 +3054,48 @@ private void showLayout(AttachAlertLayout layout, long newId) { }; if (!(currentAttachLayout instanceof ChatAttachAlertPhotoLayoutPreview || nextAttachLayout instanceof ChatAttachAlertPhotoLayoutPreview)) { - AnimatorSet animator = new AnimatorSet(); - nextAttachLayout.setAlpha(0.0f); - nextAttachLayout.setTranslationY(AndroidUtilities.dp(78)); - animator.playTogether( - ObjectAnimator.ofFloat(currentAttachLayout, View.TRANSLATION_Y, AndroidUtilities.dp(78) + t), - ObjectAnimator.ofFloat(currentAttachLayout, ATTACH_ALERT_LAYOUT_TRANSLATION, 0.0f, 1.0f), - ObjectAnimator.ofFloat(actionBar, View.ALPHA, actionBar.getAlpha(), 0f) - ); - animator.setDuration(180); - animator.setStartDelay(20); - animator.setInterpolator(CubicBezierInterpolator.DEFAULT); - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - currentAttachLayout.setAlpha(0.0f); - SpringAnimation springAnimation = new SpringAnimation(nextAttachLayout, DynamicAnimation.TRANSLATION_Y, 0); - springAnimation.getSpring().setDampingRatio(0.75f); - springAnimation.getSpring().setStiffness(500.0f); - springAnimation.addUpdateListener((animation12, value, velocity) -> { - if (nextAttachLayout == pollLayout || (isPhotoPicker && viewChangeAnimator != null)) { - updateSelectedPosition(1); - } - nextAttachLayout.onContainerTranslationUpdated(currentPanTranslationY); - containerView.invalidate(); - }); - springAnimation.addEndListener((animation1, canceled, value, velocity) -> { - onEnd.run(); - updateSelectedPosition(0); - }); - viewChangeAnimator = springAnimation; - springAnimation.start(); - } - }); - viewChangeAnimator = animator; - animator.start(); + if (animated) { + AnimatorSet animator = new AnimatorSet(); + nextAttachLayout.setAlpha(0.0f); + nextAttachLayout.setTranslationY(AndroidUtilities.dp(78)); + animator.playTogether( + ObjectAnimator.ofFloat(currentAttachLayout, View.TRANSLATION_Y, AndroidUtilities.dp(78) + t), + ObjectAnimator.ofFloat(currentAttachLayout, ATTACH_ALERT_LAYOUT_TRANSLATION, 0.0f, 1.0f), + ObjectAnimator.ofFloat(actionBar, View.ALPHA, actionBar.getAlpha(), 0f) + ); + animator.setDuration(180); + animator.setStartDelay(20); + animator.setInterpolator(CubicBezierInterpolator.DEFAULT); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + currentAttachLayout.setAlpha(0.0f); + SpringAnimation springAnimation = new SpringAnimation(nextAttachLayout, DynamicAnimation.TRANSLATION_Y, 0); + springAnimation.getSpring().setDampingRatio(0.75f); + springAnimation.getSpring().setStiffness(500.0f); + springAnimation.addUpdateListener((animation12, value, velocity) -> { + if (nextAttachLayout == pollLayout || (isPhotoPicker && viewChangeAnimator != null)) { + updateSelectedPosition(1); + } + nextAttachLayout.onContainerTranslationUpdated(currentPanTranslationY); + containerView.invalidate(); + }); + springAnimation.addEndListener((animation1, canceled, value, velocity) -> { + onEnd.run(); + updateSelectedPosition(0); + }); + viewChangeAnimator = springAnimation; + springAnimation.start(); + } + }); + viewChangeAnimator = animator; + animator.start(); + } else { + currentAttachLayout.setAlpha(0.0f); + onEnd.run(); + updateSelectedPosition(0); + containerView.invalidate(); + } } else { int width = Math.max(nextAttachLayout.getWidth(), currentAttachLayout.getWidth()); if (nextAttachLayout instanceof ChatAttachAlertPhotoLayoutPreview) { @@ -3093,40 +3120,51 @@ public void onAnimationEnd(Animator animation) { } nextAttachLayout.setAlpha(1); currentAttachLayout.setAlpha(1); - ATTACH_ALERT_LAYOUT_TRANSLATION.set(currentAttachLayout, 0.0f); - AndroidUtilities.runOnUIThread(() -> { - float fromActionBarAlpha = actionBar.getAlpha(); - boolean showActionBar = nextAttachLayout.getCurrentItemTop() <= layout.getButtonsHideOffset(); - float toActionBarAlpha = showActionBar ? 1f : 0f; - SpringAnimation springAnimation = new SpringAnimation(new FloatValueHolder(0)); - springAnimation.addUpdateListener((animation, value, velocity) -> { - float f = value / 500f; - ATTACH_ALERT_LAYOUT_TRANSLATION.set(currentAttachLayout, f); - actionBar.setAlpha(AndroidUtilities.lerp(fromActionBarAlpha, toActionBarAlpha, f)); - updateLayout(currentAttachLayout, false, 0); - updateLayout(nextAttachLayout, false, 0); - float mediaPreviewAlpha = nextAttachLayout instanceof ChatAttachAlertPhotoLayoutPreview && !showActionBar ? f : 1f - f; - mediaPreviewView.setAlpha(mediaPreviewAlpha); - selectedView.setAlpha(1f - mediaPreviewAlpha); - selectedView.setTranslationX(mediaPreviewAlpha * -AndroidUtilities.dp(16)); - mediaPreviewView.setTranslationX((1f - mediaPreviewAlpha) * AndroidUtilities.dp(16)); - }); - springAnimation.addEndListener((animation, canceled, value, velocity) -> { - currentAttachLayout.onHideShowProgress(1f); - nextAttachLayout.onHideShowProgress(1f); - currentAttachLayout.onContainerTranslationUpdated(currentPanTranslationY); - nextAttachLayout.onContainerTranslationUpdated(currentPanTranslationY); - containerView.invalidate(); - actionBar.setTag(showActionBar ? 1 : null); - onEnd.run(); - }); - springAnimation.setSpring(new SpringForce(500f)); - springAnimation.getSpring().setDampingRatio(1f); - springAnimation.getSpring().setStiffness(1000.0f); - springAnimation.start(); + boolean showActionBar = nextAttachLayout.getCurrentItemTop() <= layout.getButtonsHideOffset(); + if (animated) { + ATTACH_ALERT_LAYOUT_TRANSLATION.set(currentAttachLayout, 0.0f); + AndroidUtilities.runOnUIThread(() -> { + float fromActionBarAlpha = actionBar.getAlpha(); + float toActionBarAlpha = showActionBar ? 1f : 0f; + SpringAnimation springAnimation = new SpringAnimation(new FloatValueHolder(0)); + springAnimation.addUpdateListener((animation, value, velocity) -> { + float f = value / 500f; + ATTACH_ALERT_LAYOUT_TRANSLATION.set(currentAttachLayout, f); + actionBar.setAlpha(AndroidUtilities.lerp(fromActionBarAlpha, toActionBarAlpha, f)); + updateLayout(currentAttachLayout, false, 0); + updateLayout(nextAttachLayout, false, 0); + float mediaPreviewAlpha = nextAttachLayout instanceof ChatAttachAlertPhotoLayoutPreview && !showActionBar ? f : 1f - f; + mediaPreviewView.setAlpha(mediaPreviewAlpha); + selectedView.setAlpha(1f - mediaPreviewAlpha); + selectedView.setTranslationX(mediaPreviewAlpha * -AndroidUtilities.dp(16)); + mediaPreviewView.setTranslationX((1f - mediaPreviewAlpha) * AndroidUtilities.dp(16)); + }); + springAnimation.addEndListener((animation, canceled, value, velocity) -> { + currentAttachLayout.onHideShowProgress(1f); + nextAttachLayout.onHideShowProgress(1f); + currentAttachLayout.onContainerTranslationUpdated(currentPanTranslationY); + nextAttachLayout.onContainerTranslationUpdated(currentPanTranslationY); + containerView.invalidate(); + actionBar.setTag(showActionBar ? 1 : null); + onEnd.run(); + }); + springAnimation.setSpring(new SpringForce(500f)); + springAnimation.getSpring().setDampingRatio(1f); + springAnimation.getSpring().setStiffness(1000.0f); + springAnimation.start(); - viewChangeAnimator = springAnimation; - }); + viewChangeAnimator = springAnimation; + }); + } else { + currentAttachLayout.onHideShowProgress(1f); + nextAttachLayout.onHideShowProgress(1f); + currentAttachLayout.onContainerTranslationUpdated(currentPanTranslationY); + nextAttachLayout.onContainerTranslationUpdated(currentPanTranslationY); + containerView.invalidate(); + ATTACH_ALERT_LAYOUT_TRANSLATION.set(currentAttachLayout, 1.0f); + actionBar.setTag(showActionBar ? 1 : null); + onEnd.run(); + } } } @@ -3499,10 +3537,12 @@ protected boolean onCustomOpenAnimation() { currentSheetAnimation.setDuration(400); currentSheetAnimation.setStartDelay(20); currentSheetAnimation.setInterpolator(openInterpolator); + AnimationNotificationsLocker locker = new AnimationNotificationsLocker(); BottomSheetDelegateInterface delegate = super.delegate; final Runnable onAnimationEnd = () -> { currentSheetAnimation = null; appearSpringAnimation = null; + locker.unlock(); currentSheetAnimationType = 0; if (delegate != null) { delegate.onOpenAnimationEnd(); @@ -3541,6 +3581,7 @@ public void onAnimationCancel(Animator animation) { } } }); + locker.lock(); NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.stopAllHeavyOperations, 512); currentSheetAnimation.start(); @@ -4064,7 +4105,7 @@ public void init() { TLRPC.Chat chat = ((ChatActivity) baseFragment).getCurrentChat(); TLRPC.User user = ((ChatActivity) baseFragment).getCurrentUser(); if (chat != null) { - // mediaEnabled = ChatObject.canSendMedia(chat); + // mediaEnabled = ChatObject.canSendMedia(chat); photosEnabled = ChatObject.canSendPhoto(chat); videosEnabled = ChatObject.canSendVideo(chat); musicEnabled = ChatObject.canSendMusic(chat); @@ -4093,6 +4134,10 @@ public void init() { } selectedId = 5; layoutToSet = locationLayout; + } else if (isStoryAudioPicker) { + openAudioLayout(false); + layoutToSet = audioLayout; + selectedId = 3; } else if (isSoundPicker || isEmojiPicker) { openDocumentsLayout(false); layoutToSet = documentLayout; @@ -4240,11 +4285,13 @@ public void setEmojiPicker() { public boolean storyLocationPickerFileIsVideo; public File storyLocationPickerPhotoFile; public double[] storyLocationPickerLatLong; + public void setStoryLocationPicker() { isStoryLocationPicker = true; buttonsRecyclerView.setVisibility(View.GONE); shadow.setVisibility(View.GONE); } + public void setStoryLocationPicker(boolean isVideo, File photo) { storyLocationPickerFileIsVideo = isVideo; storyLocationPickerPhotoFile = photo; @@ -4254,12 +4301,16 @@ public void setStoryLocationPicker(boolean isVideo, File photo) { } public void setStoryLocationPicker(double lat, double lon) { - storyLocationPickerLatLong = new double[] { lat, lon }; + storyLocationPickerLatLong = new double[]{lat, lon}; isStoryLocationPicker = true; buttonsRecyclerView.setVisibility(View.GONE); shadow.setVisibility(View.GONE); } + public void setStoryAudioPicker() { + isStoryAudioPicker = true; + } + public void setMaxSelectedPhotos(int value, boolean order) { if (editingMessageObject != null) { return; @@ -4414,7 +4465,7 @@ public void notifyDataSetChanged() { attachBotsStartRow = buttonsCount; attachMenuBots.clear(); for (TLRPC.TL_attachMenuBot bot : MediaDataController.getInstance(currentAccount).getAttachMenuBots().bots) { - if (MediaDataController.canShowAttachMenuBot(bot, chatActivity.getCurrentChat() != null ? chatActivity.getCurrentChat() : chatActivity.getCurrentUser())) { + if (bot.show_in_attach_menu && MediaDataController.canShowAttachMenuBot(bot, chatActivity.getCurrentChat() != null ? chatActivity.getCurrentChat() : chatActivity.getCurrentUser())) { attachMenuBots.add(bot); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertAudioLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertAudioLayout.java index 411748cc61..a20c6c9a0b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertAudioLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertAudioLayout.java @@ -19,6 +19,7 @@ import android.os.Build; import android.provider.MediaStore; import android.text.TextUtils; +import android.util.Log; import android.util.LongSparseArray; import android.util.TypedValue; import android.view.Gravity; @@ -463,7 +464,13 @@ private void onItemClick(View view) { SharedAudioCell audioCell = (SharedAudioCell) view; MediaController.AudioEntry audioEntry = (MediaController.AudioEntry) audioCell.getTag(); boolean add; - if (selectedAudios.indexOfKey(audioEntry.id) >= 0) { + if (parentAlert.isStoryAudioPicker) { + sendPressed = true; + ArrayList audios = new ArrayList<>(); + audios.add(audioEntry.messageObject); + delegate.didSelectAudio(audios, parentAlert.commentTextView.getText(), false, 0); + add = true; + } else if (selectedAudios.indexOfKey(audioEntry.id) >= 0) { selectedAudios.remove(audioEntry.id); selectedAudiosOrder.remove(audioEntry); audioCell.setChecked(false, true); 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 2cd09ed1c5..36602417f3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertBotWebViewLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertBotWebViewLayout.java @@ -1,5 +1,7 @@ package org.telegram.ui.Components; +import static org.telegram.ui.Components.Bulletin.DURATION_PROLONG; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; @@ -179,6 +181,11 @@ public boolean dispatchTouchEvent(MotionEvent ev) { } return super.dispatchTouchEvent(ev); } + + @Override + public void onWebViewCreated() { + swipeContainer.setWebView(webViewContainer.getWebView()); + } }; swipeContainer = new WebViewSwipeContainer(context) { @Override @@ -481,7 +488,6 @@ public void requestWebView(int currentAccount, long peerId, long botId, boolean TLRPC.TL_webViewResultUrl resultUrl = (TLRPC.TL_webViewResultUrl) response; queryId = resultUrl.query_id; webViewContainer.loadUrl(currentAccount, resultUrl.url); - swipeContainer.setWebView(webViewContainer.getWebView()); AndroidUtilities.runOnUIThread(pollRunnable); } @@ -623,6 +629,34 @@ public void didReceivedNotification(int id, int account, Object... args) { } } + public void showJustAddedBulletin() { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(botId); + TLRPC.TL_attachMenuBot currentBot = null; + for (TLRPC.TL_attachMenuBot bot : MediaDataController.getInstance(currentAccount).getAttachMenuBots().bots) { + if (bot.bot_id == botId) { + currentBot = bot; + break; + } + } + if (currentBot == null) { + return; + } + String str; + if (currentBot.show_in_side_menu && currentBot.show_in_attach_menu) { + str = LocaleController.formatString("BotAttachMenuShortcatAddedAttachAndSide", R.string.BotAttachMenuShortcatAddedAttachAndSide, user.first_name); + } else if (currentBot.show_in_side_menu) { + str = LocaleController.formatString("BotAttachMenuShortcatAddedSide", R.string.BotAttachMenuShortcatAddedSide, user.first_name); + } else { + str = LocaleController.formatString("BotAttachMenuShortcatAddedAttach", R.string.BotAttachMenuShortcatAddedAttach, user.first_name); + } + AndroidUtilities.runOnUIThread(() -> { + BulletinFactory.of(parentAlert.getContainer(), resourcesProvider) + .createSimpleBulletin(R.raw.contact_check, AndroidUtilities.replaceTags(str)) + .setDuration(DURATION_PROLONG) + .show(true); + }, 200); + } + public static class WebViewSwipeContainer extends FrameLayout { public final static SimpleFloatPropertyCompat SWIPE_OFFSET_Y = new SimpleFloatPropertyCompat<>("swipeOffsetY", WebViewSwipeContainer::getSwipeOffsetY, WebViewSwipeContainer::setSwipeOffsetY); 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 48e35498c6..d797029a5e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertDocumentLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertDocumentLayout.java @@ -796,7 +796,7 @@ private boolean onItemClick(View view, Object object) { return false; } if ((item.file.length() > FileLoader.DEFAULT_MAX_FILE_SIZE && !UserConfig.getInstance(UserConfig.selectedAccount).isPremium()) || item.file.length() > FileLoader.DEFAULT_MAX_FILE_SIZE_PREMIUM) { - LimitReachedBottomSheet limitReachedBottomSheet = new LimitReachedBottomSheet(parentAlert.baseFragment, parentAlert.getContainer().getContext(), LimitReachedBottomSheet.TYPE_LARGE_FILE, UserConfig.selectedAccount); + LimitReachedBottomSheet limitReachedBottomSheet = new LimitReachedBottomSheet(parentAlert.baseFragment, parentAlert.getContainer().getContext(), LimitReachedBottomSheet.TYPE_LARGE_FILE, UserConfig.selectedAccount, null); limitReachedBottomSheet.setVeryLargeFile(true); limitReachedBottomSheet.show(); return false; 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 533aacd8ab..de0b59040c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java @@ -468,7 +468,7 @@ public void onApplyCaption(CharSequence caption) { MediaController.SearchImage photoEntry1 = (MediaController.SearchImage) o; firstPhotoCaption = photoEntry1.caption; } - parentAlert.commentTextView.setText(firstPhotoCaption); + parentAlert.commentTextView.setText(AnimatedEmojiSpan.cloneSpans(firstPhotoCaption, AnimatedEmojiDrawable.CACHE_TYPE_ALERT_PREVIEW)); } } @@ -1778,16 +1778,6 @@ protected void openPhotoViewer(MediaController.PhotoEntry entry, final boolean s } PhotoViewer.getInstance().openPhotoForSelect(arrayList, index, type, false, new BasePhotoProvider() { - @Override - public void onOpen() { - pauseCameraPreview(); - } - - @Override - public void onClose() { - resumeCameraPreview(); - } - @Override public ImageReceiver.BitmapHolder getThumbForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index) { return null; 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 e595f08665..f7d9b3d291 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAvatarContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAvatarContainer.java @@ -13,6 +13,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; +import android.graphics.Canvas; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.BitmapDrawable; @@ -51,6 +52,7 @@ import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ChatActivity; import org.telegram.ui.ProfileActivity; +import org.telegram.ui.Stories.StoriesUtilities; import org.telegram.ui.TopicsFragment; import java.util.concurrent.atomic.AtomicReference; @@ -59,7 +61,8 @@ public class ChatAvatarContainer extends FrameLayout implements NotificationCenter.NotificationCenterDelegate { - private BackupImageView avatarImageView; + public boolean allowDrawStories; + public BackupImageView avatarImageView; private SimpleTextView titleTextView; private AtomicReference titleTextLargerCopyView = new AtomicReference<>(); private SimpleTextView subtitleTextView; @@ -99,6 +102,10 @@ public class ChatAvatarContainer extends FrameLayout implements NotificationCent private AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable emojiStatusDrawable; + public void hideSubtitle() { + subtitleTextView.setVisibility(View.GONE); + } + private class SimpleTextConnectedView extends SimpleTextView { private AtomicReference reference; @@ -464,7 +471,9 @@ private void fadeOutToLessWidth(int largerWidth) { if (subtitleTextLargerCopyView2 != null) { removeView(subtitleTextLargerCopyView2); this.subtitleTextLargerCopyView.set(null); - setClipChildren(true); + if (!allowDrawStories) { + setClipChildren(true); + } } }).start(); addView(subtitleTextLargerCopyView); 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 1dcbe3cee9..5786c6e6a5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatThemeBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatThemeBottomSheet.java @@ -402,16 +402,17 @@ private void updateState(boolean animated) { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - ChatThemeController.preloadAllWallpaperThumbs(true); - ChatThemeController.preloadAllWallpaperThumbs(false); - ChatThemeController.preloadAllWallpaperImages(true); - ChatThemeController.preloadAllWallpaperImages(false); + ChatThemeController chatThemeController = ChatThemeController.getInstance(currentAccount); + chatThemeController.preloadAllWallpaperThumbs(true); + chatThemeController.preloadAllWallpaperThumbs(false); + chatThemeController.preloadAllWallpaperImages(true); + chatThemeController.preloadAllWallpaperImages(false); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded); isApplyClicked = false; List cachedThemes = themeDelegate.getCachedThemes(); if (cachedThemes == null || cachedThemes.isEmpty()) { - ChatThemeController.requestAllChatThemes(new ResultCallback>() { + chatThemeController.requestAllChatThemes(new ResultCallback>() { @Override public void onComplete(List result) { if (result != null && !result.isEmpty()) { 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 d96429fda6..8860a524eb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ClippingImageView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ClippingImageView.java @@ -294,6 +294,10 @@ public void setImageBitmap(ImageReceiver.BitmapHolder bitmap) { invalidate(); } + public ImageReceiver.BitmapHolder getBitmapHolder() { + return bmp; + } + public Bitmap getBitmap() { return bmp != null ? bmp.bitmap : null; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/CombinedDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/CombinedDrawable.java index 4359b7e430..f7a6cf29c3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/CombinedDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/CombinedDrawable.java @@ -62,6 +62,16 @@ public CombinedDrawable(Drawable backgroundDrawable, Drawable iconDrawable) { } } + public void setBackgroundDrawable(Drawable backgroundDrawable) { + background = backgroundDrawable; + invalidateSelf(); + } + + public void setIconDrawable(Drawable iconDrawable) { + icon = iconDrawable; + invalidateSelf(); + } + public void setCustomSize(int width, int height) { backWidth = width; backHeight = height; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/DrawingInBackgroundThreadDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/DrawingInBackgroundThreadDrawable.java index 64d825ba7f..ef7fd9a17e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/DrawingInBackgroundThreadDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/DrawingInBackgroundThreadDrawable.java @@ -62,7 +62,7 @@ public void run() { backgroundCanvas.translate(0, padding); drawInBackground(backgroundCanvas); backgroundCanvas.restore(); - + backgroundBitmap.prepareToDraw(); } catch (Exception e) { FileLog.e(e); error = true; 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 6b8e359d63..c0b45c41d1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextBoldCursor.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextBoldCursor.java @@ -52,6 +52,7 @@ import org.telegram.messenger.FileLog; import org.telegram.messenger.LocaleController; import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; import org.telegram.messenger.XiaomiUtilities; import org.telegram.ui.ActionBar.FloatingActionMode; import org.telegram.ui.ActionBar.FloatingToolbar; @@ -80,6 +81,7 @@ public class EditTextBoldCursor extends EditTextEffects { private GradientDrawable gradientDrawable; private SubstringLayoutAnimator hintAnimator; + float rightHintOffset; private Runnable invalidateRunnable = new Runnable() { @Override @@ -102,7 +104,11 @@ public void run() { private float lineSpacingExtra; private Rect rect = new Rect(); private StaticLayout hintLayout; + public float hintLayoutX, hintLayoutY; + public boolean hintLayoutYFix; + public Utilities.Callback2 drawHint; private AnimatedTextView.AnimatedTextDrawable hintAnimatedDrawable; + private AnimatedTextView.AnimatedTextDrawable hintAnimatedDrawable2; private CharSequence hint; private StaticLayout errorLayout; private CharSequence errorText; @@ -207,6 +213,20 @@ private void addUndoRedo(Menu menu) { } } + public void setHintText2(CharSequence text, boolean animated) { + if (hintAnimatedDrawable2 != null) { + hintAnimatedDrawable2.setText(text, !LocaleController.isRTL && animated); + } + } + + public void setHintRightOffset(int rightHintOffset) { + if (this.rightHintOffset == rightHintOffset) { + return; + } + this.rightHintOffset = rightHintOffset; + invalidate(); + } + @TargetApi(23) private class ActionModeCallback2Wrapper extends ActionMode.Callback2 { private final ActionMode.Callback mWrapped; @@ -258,8 +278,19 @@ public void invalidateSelf() { invalidate(); } }; + hintAnimatedDrawable.setEllipsizeByGradient(true); hintAnimatedDrawable.setTextColor(hintColor); hintAnimatedDrawable.setTextSize(getPaint().getTextSize()); + + hintAnimatedDrawable2 = new AnimatedTextView.AnimatedTextDrawable() { + @Override + public void invalidateSelf() { + invalidate(); + } + }; + hintAnimatedDrawable2.setGravity(Gravity.RIGHT); + hintAnimatedDrawable2.setTextColor(hintColor); + hintAnimatedDrawable2.setTextSize(getPaint().getTextSize()); } @Override @@ -534,6 +565,9 @@ public void setHintColor(int value) { if (hintAnimatedDrawable != null) { hintAnimatedDrawable.setTextColor(hintColor); } + if (hintAnimatedDrawable2 != null) { + hintAnimatedDrawable2.setTextColor(hintColor); + } invalidate(); } @@ -547,6 +581,9 @@ public void setTextSize(int unit, float size) { if (hintAnimatedDrawable != null) { hintAnimatedDrawable.setTextSize(AndroidUtilities.dp(size)); } + if (hintAnimatedDrawable2 != null) { + hintAnimatedDrawable2.setTextSize(AndroidUtilities.dp(size)); + } super.setTextSize(unit, size); } @@ -604,11 +641,18 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (hintAnimatedDrawable != null) { hintAnimatedDrawable.setBounds(getPaddingLeft(), getPaddingTop(), getMeasuredWidth() - getPaddingRight(), getMeasuredHeight() - getPaddingBottom()); } + if (hintAnimatedDrawable2 != null) { + hintAnimatedDrawable2.setBounds(getPaddingLeft(), getPaddingTop(), getMeasuredWidth() - getPaddingRight(), getMeasuredHeight() - getPaddingBottom()); + } if (hintLayout != null && hintAnimatedDrawable == null) { if (lastSize != currentSize) { - setHintText(hint); + setHintText(hint, false, hintLayout.getPaint()); + } + if (hintLayoutYFix) { + lineY = getExtendedPaddingTop() + getPaddingTop() + (getMeasuredHeight() - getPaddingTop() - getPaddingBottom() - hintLayout.getHeight()) / 2.0f + hintLayout.getHeight() - AndroidUtilities.dp(1); + } else { + lineY = (getMeasuredHeight() - hintLayout.getHeight()) / 2.0f + hintLayout.getHeight() + AndroidUtilities.dp(6); } - lineY = (getMeasuredHeight() - hintLayout.getHeight()) / 2.0f + hintLayout.getHeight() + AndroidUtilities.dp(6); } else { lineY = getMeasuredHeight() - AndroidUtilities.dp(2); } @@ -616,10 +660,14 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } public void setHintText(CharSequence text) { - setHintText(text, false); + setHintText(text, false, getPaint()); } public void setHintText(CharSequence text, boolean animated) { + setHintText(text, animated, getPaint()); + } + + public void setHintText(CharSequence text, boolean animated, TextPaint paint) { if (hintAnimatedDrawable != null) { hintAnimatedDrawable.setText(text, !LocaleController.isRTL); } else { @@ -633,7 +681,7 @@ public void setHintText(CharSequence text, boolean animated) { if (hintAnimator == null) { hintAnimator = new SubstringLayoutAnimator(this); } - hintAnimator.create(hintLayout, hint, text, getPaint()); + hintAnimator.create(hintLayout, hint, text, paint); } else { if (hintAnimator != null) { hintAnimator.cancel(); @@ -641,12 +689,12 @@ public void setHintText(CharSequence text, boolean animated) { } hint = text; if (getMeasuredWidth() != 0) { - text = TextUtils.ellipsize(text, getPaint(), getMeasuredWidth(), TextUtils.TruncateAt.END); + text = TextUtils.ellipsize(text, paint, getMeasuredWidth(), TextUtils.TruncateAt.END); if (hintLayout != null && TextUtils.equals(hintLayout.getText(), text)) { return; } } - hintLayout = new StaticLayout(text, getPaint(), AndroidUtilities.dp(1000), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + hintLayout = new StaticLayout(text, paint, AndroidUtilities.dp(1000), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); } } @@ -704,9 +752,9 @@ public void setLineSpacing(float add, float mult) { @Override public int getExtendedPaddingTop() { - if (ignoreClipTop) { - return 0; - } +// if (ignoreClipTop) { +// return 0; +// } if (ignoreTopCount != 0) { ignoreTopCount--; return 0; @@ -750,75 +798,103 @@ public void invalidateForce() { } catch (Exception ignore) {}; } - @Override - protected void onDraw(Canvas canvas) { - if (length() == 0 || transformHintToHeader) { - if (hintVisible && hintAlpha != 1.0f || !hintVisible && hintAlpha != 0.0f) { - long newTime = System.currentTimeMillis(); - long dt = newTime - hintLastUpdateTime; - if (dt < 0 || dt > 17) { - dt = 17; + private void drawHint(Canvas canvas) { + if (length() != 0 && !transformHintToHeader) { + return; + } + if (hintVisible && hintAlpha != 1.0f || !hintVisible && hintAlpha != 0.0f) { + long newTime = System.currentTimeMillis(); + long dt = newTime - hintLastUpdateTime; + if (dt < 0 || dt > 17) { + dt = 17; + } + hintLastUpdateTime = newTime; + if (hintVisible) { + hintAlpha += dt / 150.0f; + if (hintAlpha > 1.0f) { + hintAlpha = 1.0f; } - hintLastUpdateTime = newTime; - if (hintVisible) { - hintAlpha += dt / 150.0f; - if (hintAlpha > 1.0f) { - hintAlpha = 1.0f; - } - } else { - hintAlpha -= dt / 150.0f; - if (hintAlpha < 0.0f) { - hintAlpha = 0.0f; - } + } else { + hintAlpha -= dt / 150.0f; + if (hintAlpha < 0.0f) { + hintAlpha = 0.0f; } - invalidate(); } - if (hintAnimatedDrawable != null && !TextUtils.isEmpty(hintAnimatedDrawable.getText()) && (hintVisible || hintAlpha != 0)) { - hintAnimatedDrawable.setAlpha((int) (Color.alpha(hintColor) * hintAlpha)); - hintAnimatedDrawable.draw(canvas); - } else if (hintLayout != null && (hintVisible || hintAlpha != 0)) { - int oldColor = getPaint().getColor(); - - canvas.save(); - int left = 0; - float lineLeft = hintLayout.getLineLeft(0); - float hintWidth = hintLayout.getLineWidth(0); - if (lineLeft != 0) { - left -= lineLeft; - } - if (supportRtlHint && LocaleController.isRTL) { - float offset = getMeasuredWidth() - hintWidth; - canvas.translate(left + getScrollX() + offset, lineY - hintLayout.getHeight() - AndroidUtilities.dp(7)); + invalidate(); + } + if (hintAnimatedDrawable != null && !TextUtils.isEmpty(hintAnimatedDrawable.getText()) && (hintVisible || hintAlpha != 0)) { + if (hintAnimatedDrawable2 != null) { + if (hintAnimatedDrawable.getCurrentWidth() + hintAnimatedDrawable2.getCurrentWidth() < getMeasuredWidth()) { + canvas.save(); + canvas.translate(hintAnimatedDrawable2.getCurrentWidth() - getMeasuredWidth() + hintAnimatedDrawable.getCurrentWidth(), 0); + hintAnimatedDrawable2.setAlpha((int) (Color.alpha(hintColor) * hintAlpha)); + hintAnimatedDrawable2.draw(canvas); + canvas.restore(); + hintAnimatedDrawable.setRightPadding(0); } else { - canvas.translate(left + getScrollX(), lineY - hintLayout.getHeight() - AndroidUtilities.dp2(7)); + canvas.save(); + canvas.translate(rightHintOffset, 0); + hintAnimatedDrawable2.setAlpha((int) (Color.alpha(hintColor) * hintAlpha)); + hintAnimatedDrawable2.draw(canvas); + canvas.restore(); + hintAnimatedDrawable.setRightPadding(hintAnimatedDrawable2.getCurrentWidth() + AndroidUtilities.dp(2) - rightHintOffset); } - if (transformHintToHeader) { - float scale = 1.0f - 0.3f * headerAnimationProgress; + } else { + hintAnimatedDrawable.setRightPadding(0); + } + hintAnimatedDrawable.setAlpha((int) (Color.alpha(hintColor) * hintAlpha)); + hintAnimatedDrawable.draw(canvas); + } else if (hintLayout != null && (hintVisible || hintAlpha != 0)) { + int oldColor = getPaint().getColor(); - if (supportRtlHint && LocaleController.isRTL) { - canvas.translate((hintWidth + lineLeft) - (hintWidth + lineLeft) * scale, 0); - } else if (lineLeft != 0) { - canvas.translate(lineLeft * (1.0f - scale), 0); - } - canvas.scale(scale, scale); - canvas.translate(0, -AndroidUtilities.dp(22) * headerAnimationProgress); - getPaint().setColor(ColorUtils.blendARGB(hintColor, headerHintColor, headerAnimationProgress)); - } else { - getPaint().setColor(hintColor); - getPaint().setAlpha((int) (255 * hintAlpha * (Color.alpha(hintColor) / 255.0f))); + canvas.save(); + int left = 0; + float lineLeft = hintLayout.getLineLeft(0); + float hintWidth = hintLayout.getLineWidth(0); + if (lineLeft != 0) { + left -= lineLeft; + } + if (supportRtlHint && LocaleController.isRTL) { + float offset = getMeasuredWidth() - hintWidth; + canvas.translate(hintLayoutX = left + getScrollX() + offset, hintLayoutY = lineY - hintLayout.getHeight() - AndroidUtilities.dp(7)); + } else { + canvas.translate(hintLayoutX = left + getScrollX(), hintLayoutY = lineY - hintLayout.getHeight() - AndroidUtilities.dp2(7)); + } + if (transformHintToHeader) { + float scale = 1.0f - 0.3f * headerAnimationProgress; + + if (supportRtlHint && LocaleController.isRTL) { + canvas.translate((hintWidth + lineLeft) - (hintWidth + lineLeft) * scale, 0); + } else if (lineLeft != 0) { + canvas.translate(lineLeft * (1.0f - scale), 0); } - if (hintAnimator != null && hintAnimator.animateTextChange) { - canvas.save(); - canvas.clipRect(0, 0, getMeasuredWidth(), getMeasuredHeight()); - hintAnimator.draw(canvas, getPaint()); - canvas.restore(); + canvas.scale(scale, scale); + canvas.translate(0, -AndroidUtilities.dp(22) * headerAnimationProgress); + getPaint().setColor(ColorUtils.blendARGB(hintColor, headerHintColor, headerAnimationProgress)); + } else { + getPaint().setColor(hintColor); + getPaint().setAlpha((int) (255 * hintAlpha * (Color.alpha(hintColor) / 255.0f))); + } + if (hintAnimator != null && hintAnimator.animateTextChange) { + canvas.save(); + canvas.clipRect(0, 0, getMeasuredWidth(), getMeasuredHeight()); + hintAnimator.draw(canvas, getPaint()); + canvas.restore(); + } else { + if (drawHint != null) { + drawHint.run(canvas, () -> hintLayout.draw(canvas)); } else { hintLayout.draw(canvas); } - getPaint().setColor(oldColor); - canvas.restore(); } + getPaint().setColor(oldColor); + canvas.restore(); } + } + + @Override + protected void onDraw(Canvas canvas) { + drawHint(canvas); int topPadding = getExtendedPaddingTop(); scrollY = Integer.MAX_VALUE; 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 ac34000208..1850a5d473 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextCaption.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextCaption.java @@ -51,6 +51,7 @@ import org.telegram.tgnet.TLRPC; import org.telegram.messenger.utils.CopyUtilities; import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.AlertDialogDecor; import org.telegram.ui.ActionBar.Theme; import java.util.List; @@ -84,6 +85,8 @@ public class EditTextCaption extends EditTextBoldCursor { private int lineCount; private boolean isInitLineCount; private final Theme.ResourcesProvider resourcesProvider; + private AlertDialog creationLinkDialog; + public boolean adaptiveCreateLinkDialog; public interface EditTextCaptionDelegate { void onSpansChanged(); @@ -404,7 +407,12 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { public void makeSelectedUrl() { - AlertDialog.Builder builder = new AlertDialog.Builder(getContext(), resourcesProvider); + AlertDialog.Builder builder; + if (adaptiveCreateLinkDialog) { + builder = new AlertDialogDecor.Builder(getContext(), resourcesProvider); + } else { + builder = new AlertDialog.Builder(getContext(), resourcesProvider); + } builder.setTitle(LocaleController.getString("CreateLink", R.string.CreateLink)); final EditTextBoldCursor editText = new EditTextBoldCursor(getContext()) { @@ -468,22 +476,55 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - builder.show().setOnShowListener(dialog -> { - editText.requestFocus(); - AndroidUtilities.showKeyboard(editText); - }); - if (editText != null) { - ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) editText.getLayoutParams(); - if (layoutParams != null) { - if (layoutParams instanceof FrameLayout.LayoutParams) { - ((FrameLayout.LayoutParams) layoutParams).gravity = Gravity.CENTER_HORIZONTAL; + if (adaptiveCreateLinkDialog) { + creationLinkDialog = builder.create(); + creationLinkDialog.setOnDismissListener(dialog -> { + creationLinkDialog = null; + requestFocus(); + }); + creationLinkDialog.setOnShowListener(dialog -> { + editText.requestFocus(); + AndroidUtilities.showKeyboard(editText); + }); + creationLinkDialog.showDelayed(250); + if (editText != null) { + ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) editText.getLayoutParams(); + if (layoutParams != null) { + if (layoutParams instanceof FrameLayout.LayoutParams) { + ((FrameLayout.LayoutParams) layoutParams).gravity = Gravity.CENTER_HORIZONTAL; + } + layoutParams.rightMargin = layoutParams.leftMargin = AndroidUtilities.dp(24); + layoutParams.height = AndroidUtilities.dp(36); + editText.setLayoutParams(layoutParams); } - layoutParams.rightMargin = layoutParams.leftMargin = AndroidUtilities.dp(24); - layoutParams.height = AndroidUtilities.dp(36); - editText.setLayoutParams(layoutParams); + editText.setSelection(0, editText.getText().length()); } - editText.setSelection(0, editText.getText().length()); + } else { + builder.show().setOnShowListener(dialog -> { + editText.requestFocus(); + AndroidUtilities.showKeyboard(editText); + }); + if (editText != null) { + ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) editText.getLayoutParams(); + if (layoutParams != null) { + if (layoutParams instanceof FrameLayout.LayoutParams) { + ((FrameLayout.LayoutParams) layoutParams).gravity = Gravity.CENTER_HORIZONTAL; + } + layoutParams.rightMargin = layoutParams.leftMargin = AndroidUtilities.dp(24); + layoutParams.height = AndroidUtilities.dp(36); + editText.setLayoutParams(layoutParams); + } + editText.setSelection(0, editText.getText().length()); + } + } + } + + public boolean closeCreationLinkDialog() { + if (creationLinkDialog != null && creationLinkDialog.isShowing()) { + creationLinkDialog.dismiss(); + return true; } + return false; } public void makeSelectedRegular() { @@ -786,7 +827,7 @@ public boolean onTextContextMenuItem(int id) { } } int start = Math.max(0, getSelectionStart()); - int end = Math.min(getText().length(), getSelectionEnd()); + int end = Math.min(getText().length(), getSelectionEnd()); setText(getText().replace(start, end, pasted)); setSelection(start + pasted.length(), start + pasted.length()); return 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 c13ed1ec4f..16c8cefa2f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextEmoji.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextEmoji.java @@ -10,6 +10,7 @@ import android.graphics.Canvas; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; import android.os.Build; import android.text.Editable; import android.text.InputFilter; @@ -82,6 +83,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; + public static final int STYLE_PHOTOVIEWER = 3; private boolean waitingForKeyboardOpen; private boolean isAnimatePopupClosing; @@ -170,13 +172,19 @@ protected int getActionModeStyle() { @Override protected void extendActionMode(ActionMode actionMode, Menu menu) { - if (style == STYLE_STORY) { + if (style == STYLE_STORY || style == STYLE_PHOTOVIEWER) { ChatActivity.fillActionModeMenu(menu, null); } super.extendActionMode(actionMode, menu); } + + @Override + public void scrollTo(int x, int y) { + if (EditTextEmoji.this.onScrollYChange(y)) { + super.scrollTo(x, y); + } + } }; - 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.setFocusable(editText.isEnabled()); @@ -184,16 +192,19 @@ protected void extendActionMode(ActionMode actionMode, Menu menu) { editText.setCursorWidth(1.5f); editText.setCursorColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); if (style == STYLE_FRAGMENT) { + editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); 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_text_RedRegular)); editText.setHintTextColor(getThemedColor(Theme.key_windowBackgroundWhiteHintText)); editText.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); + editText.setHandlesColor(getThemedColor(Theme.key_chat_TextSelectionCursor)); 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); + } else if (style == STYLE_STORY || style == STYLE_PHOTOVIEWER) { + editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + editText.setMaxLines(8); editText.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT); editText.setAllowTextEntitiesIntersection(true); editText.setHintTextColor(0x8cffffff); @@ -208,6 +219,7 @@ protected void extendActionMode(ActionMode actionMode, Menu menu) { editText.setTextIsSelectable(true); addView(editText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 40, 0, 24, 0)); } else { + editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); editText.setMaxLines(4); editText.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT); editText.setHintTextColor(getThemedColor(Theme.key_dialogTextHint)); @@ -217,14 +229,21 @@ protected void extendActionMode(ActionMode actionMode, Menu menu) { addView(editText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 48, 0, 0, 0)); } - emojiButton = new ImageView(context); + emojiButton = new ImageView(context) { + @Override + protected void dispatchDraw(Canvas canvas) { + if (!customEmojiButtonDraw(canvas, emojiButton, emojiIconDrawable)) { + super.dispatchDraw(canvas); + } + } + }; emojiButton.setScaleType(ImageView.ScaleType.CENTER_INSIDE); emojiButton.setImageDrawable(emojiIconDrawable = new ReplaceableIconDrawable(context)); 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) { + } else if (style == STYLE_STORY || style == STYLE_PHOTOVIEWER) { 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)); @@ -237,7 +256,7 @@ protected void extendActionMode(ActionMode actionMode, Menu menu) { emojiButton.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_listSelector))); } emojiButton.setOnClickListener(view -> { - if (!emojiButton.isEnabled() || (adjustPanLayoutHelper != null && adjustPanLayoutHelper.animationInProgress())) { + if (!emojiButton.isEnabled() || emojiButton.getAlpha() < 0.5f || (adjustPanLayoutHelper != null && adjustPanLayoutHelper.animationInProgress())) { return; } if (!isPopupShowing()) { @@ -263,6 +282,14 @@ public void setSuggestionsEnabled(boolean enabled) { } } + protected boolean onScrollYChange(int scrollY) { + return true; + } + + protected boolean customEmojiButtonDraw(Canvas canvas, View button, Drawable drawable) { + return false; + } + protected void onLineCountChanged(int oldLineCount, int newLineCount) { } @@ -353,7 +380,7 @@ 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) { + } else if (currentStyle == STYLE_STORY || currentStyle == STYLE_PHOTOVIEWER) { editText.setHintTextColor(0x8cffffff); editText.setTextColor(0xffffffff); editText.setCursorColor(0xffffffff); @@ -412,6 +439,9 @@ public void hidePopup(boolean byBackButton) { animator.addUpdateListener(animation -> { float v = (float) animation.getAnimatedValue(); emojiView.setTranslationY(v); + if (finalHeight > 0 && currentStyle == STYLE_STORY) { + emojiView.setAlpha(1f - v / (float) finalHeight); + } bottomPanelTranslationY(v - finalHeight); }); isAnimatePopupClosing = true; @@ -420,6 +450,7 @@ public void hidePopup(boolean byBackButton) { public void onAnimationEnd(Animator animation) { isAnimatePopupClosing = false; emojiView.setTranslationY(0); + emojiView.setAlpha(0); bottomPanelTranslationY(0); hideEmojiView(); } @@ -474,7 +505,6 @@ protected void showPopup(int show) { emojiView.setVisibility(VISIBLE); emojiViewVisible = true; - onEmojiKeyboardUpdate(); View currentView = emojiView; if (keyboardHeight <= 0) { @@ -505,25 +535,31 @@ protected void showPopup(int show) { emojiIconDrawable.setIcon(R.drawable.input_keyboard, true); onWindowSizeChanged(); } + onEmojiKeyboardUpdate(); if (!keyboardVisible && !emojiWasVisible) { ValueAnimator animator = ValueAnimator.ofFloat(emojiPadding, 0); animator.addUpdateListener(animation -> { float v = (float) animation.getAnimatedValue(); emojiView.setTranslationY(v); + if (emojiPadding > 0 && currentStyle == STYLE_STORY) { + emojiView.setAlpha(1f - v / (float) emojiPadding); + } bottomPanelTranslationY(v); }); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { emojiView.setTranslationY(0); + emojiView.setAlpha(1f); bottomPanelTranslationY(0); } }); animator.setDuration(AdjustPanLayoutHelper.keyboardDuration); animator.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); animator.start(); - + } else { + emojiView.setAlpha(1f); } } else { if (emojiButton != null) { @@ -677,6 +713,7 @@ public void onCustomEmojiSelected(long documentId, TLRPC.Document document, Str } else { span = new AnimatedEmojiSpan(documentId, editText.getPaint().getFontMetricsInt()); } + span.cacheType = emojiView.emojiCacheType; spannable.setSpan(span, 0, spannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); editText.setText(editText.getText().insert(i, spannable)); int j = i + spannable.length(); @@ -722,7 +759,7 @@ public int getKeyboardHeight() { @Override public void onSizeChanged(int height, boolean isWidthGreater) { - if (height > AndroidUtilities.dp(50) && (keyboardVisible || currentStyle == STYLE_STORY) && !AndroidUtilities.isInMultiwindow && !AndroidUtilities.isTablet()) { + if (height > AndroidUtilities.dp(50) && (keyboardVisible || currentStyle == STYLE_STORY || currentStyle == STYLE_PHOTOVIEWER) && !AndroidUtilities.isInMultiwindow && !AndroidUtilities.isTablet()) { if (isWidthGreater) { keyboardHeightLand = height; MessagesController.getGlobalEmojiSettings().edit().putInt("kbd_height_land3", keyboardHeightLand).apply(); 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 7054d692e9..f6350368a7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/FilterGLThread.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/FilterGLThread.java @@ -1,6 +1,7 @@ package org.telegram.ui.Components; import android.graphics.Bitmap; +import android.graphics.Matrix; import android.graphics.SurfaceTexture; import android.opengl.GLES11Ext; import android.opengl.GLES20; @@ -37,6 +38,7 @@ public class FilterGLThread extends DispatchQueue { private EGLContext eglContext; private EGLSurface eglSurface; private boolean initied; + private boolean isVideo; private volatile int surfaceWidth; private volatile int surfaceHeight; @@ -50,6 +52,10 @@ public class FilterGLThread extends DispatchQueue { private int[] videoTexture = new int[1]; private boolean videoFrameAvailable; + private boolean uiBlurEnabled; + private final BlurringShader.BlurManager blurManager; + private BlurringShader uiBlur; + private FilterShaders filterShaders; private int simpleShaderProgram; @@ -57,6 +63,12 @@ public class FilterGLThread extends DispatchQueue { private int simpleInputTexCoordHandle; private int simpleSourceImageHandle; + private int simpleOESShaderProgram; + private int simpleOESPositionHandle; + private int simpleOESMatrixHandle; + private int simpleOESInputTexCoordHandle; + private int simpleOESSourceImageHandle; + private boolean blurred; private int renderBufferWidth; private int renderBufferHeight; @@ -75,16 +87,21 @@ public interface FilterGLThreadVideoDelegate { private FilterGLThreadVideoDelegate videoDelegate; - 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) { + public FilterGLThread(SurfaceTexture surface, Bitmap bitmap, int bitmapOrientation, boolean mirror, StoryEntry.HDRInfo hdrInfo, boolean allowBitmapScaling, BlurringShader.BlurManager blurManager, int w, int h) { super("PhotoFilterGLThread", false); surfaceTexture = surface; + surfaceWidth = w; + surfaceHeight = h; currentBitmap = bitmap; orientation = bitmapOrientation; - filterShaders = new FilterShaders(false, hdrInfo); + this.blurManager = blurManager; + uiBlurEnabled = blurManager != null; + if (uiBlurEnabled) { + uiBlur = new BlurringShader(this); + uiBlur.setBlurManager(blurManager); + } + + filterShaders = new FilterShaders(isVideo = false, hdrInfo); filterShaders.setScaleBitmap(allowBitmapScaling); float[] textureCoordinates = { @@ -112,11 +129,19 @@ public FilterGLThread(SurfaceTexture surface, Bitmap bitmap, int bitmapOrientati start(); } - public FilterGLThread(SurfaceTexture surface, FilterGLThreadVideoDelegate filterGLThreadVideoDelegate, StoryEntry.HDRInfo hdrInfo) { + public FilterGLThread(SurfaceTexture surface, FilterGLThreadVideoDelegate filterGLThreadVideoDelegate, StoryEntry.HDRInfo hdrInfo, BlurringShader.BlurManager blurManager, int w, int h) { super("VideoFilterGLThread", false); surfaceTexture = surface; + surfaceWidth = w; + surfaceHeight = h; videoDelegate = filterGLThreadVideoDelegate; - filterShaders = new FilterShaders(true, hdrInfo); + this.blurManager = blurManager; + uiBlurEnabled = blurManager != null; + if (uiBlurEnabled) { + uiBlur = new BlurringShader(this); + uiBlur.setBlurManager(blurManager); + } + filterShaders = new FilterShaders(isVideo = true, hdrInfo); start(); } @@ -182,7 +207,8 @@ private boolean initGL() { } int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; - eglContext = egl10.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); + EGLContext parentContext = blurManager != null ? blurManager.getParentContext() : EGL10.EGL_NO_CONTEXT; + eglContext = egl10.eglCreateContext(eglDisplay, eglConfig, parentContext, attrib_list); if (eglContext == null) { if (BuildVars.LOGS_ENABLED) { FileLog.e("eglCreateContext failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); @@ -190,6 +216,9 @@ private boolean initGL() { finish(); return false; } + if (blurManager != null) { + blurManager.acquiredContext(eglContext); + } if (surfaceTexture instanceof SurfaceTexture) { eglSurface = egl10.eglCreateWindowSurface(eglDisplay, eglConfig, surfaceTexture, null); @@ -237,6 +266,31 @@ private boolean initGL() { return false; } + vertexShader = FilterShaders.loadShader(GLES20.GL_VERTEX_SHADER, FilterShaders.simpleVertexVideoShaderCode); + fragmentShader = FilterShaders.loadShader(GLES20.GL_FRAGMENT_SHADER, "#extension GL_OES_EGL_image_external : require\n" + FilterShaders.simpleFragmentShaderCode.replace("sampler2D", "samplerExternalOES")); + if (vertexShader != 0 && fragmentShader != 0) { + simpleOESShaderProgram = GLES20.glCreateProgram(); + GLES20.glAttachShader(simpleOESShaderProgram, vertexShader); + GLES20.glAttachShader(simpleOESShaderProgram, fragmentShader); + GLES20.glBindAttribLocation(simpleOESShaderProgram, 0, "position"); + GLES20.glBindAttribLocation(simpleOESShaderProgram, 1, "inputTexCoord"); + + GLES20.glLinkProgram(simpleOESShaderProgram); + int[] linkStatus = new int[1]; + GLES20.glGetProgramiv(simpleOESShaderProgram, GLES20.GL_LINK_STATUS, linkStatus, 0); + if (linkStatus[0] == 0) { + GLES20.glDeleteProgram(simpleOESShaderProgram); + simpleOESShaderProgram = 0; + } else { + simpleOESPositionHandle = GLES20.glGetAttribLocation(simpleOESShaderProgram, "position"); + simpleOESInputTexCoordHandle = GLES20.glGetAttribLocation(simpleOESShaderProgram, "inputTexCoord"); + simpleOESSourceImageHandle = GLES20.glGetUniformLocation(simpleOESShaderProgram, "sourceImage"); + simpleOESMatrixHandle = GLES20.glGetUniformLocation(simpleOESShaderProgram, "videoMatrix"); + } + } else { + return false; + } + int w; int h; if (currentBitmap != null) { @@ -263,6 +317,14 @@ private boolean initGL() { AndroidUtilities.runOnUIThread(() -> videoDelegate.onVideoSurfaceCreated(videoSurfaceTexture)); } + if (uiBlurEnabled && uiBlur != null) { + if (!uiBlur.setup(surfaceWidth / (float) surfaceHeight, true, blurManager.padding)) { + FileLog.e("Failed to create uiBlurFramebuffer"); + uiBlurEnabled = false; + uiBlur = null; + } + } + if (!filterShaders.create()) { finish(); return false; @@ -295,9 +357,13 @@ public void setVideoSize(int width, int height) { break; case SharedConfig.PERFORMANCE_CLASS_LOW: default: - maxSide = 1280; + maxSide = 720; break; } + if (SharedConfig.getDevicePerformanceClass() == SharedConfig.PERFORMANCE_CLASS_LOW && (videoWidth > 1280 || videoHeight > 1280)) { + videoWidth /= 2; + videoHeight /= 2; + } if (videoWidth > maxSide || videoHeight > maxSide) { if (videoWidth > videoHeight) { videoHeight = (int) (videoHeight / ((float) maxSide / videoWidth)); @@ -321,6 +387,9 @@ public void finish() { eglSurface = null; } if (eglContext != null) { + if (blurManager != null) { + blurManager.destroyedContext(eglContext); + } egl10.eglDestroyContext(eglDisplay, eglContext); eglContext = null; } @@ -356,55 +425,82 @@ private void makeCurrentContext() { private boolean filterTextureAvailable; - private Runnable drawRunnable = new Runnable() { - @Override - public void run() { - if (!initied) { - return; - } + private final Runnable drawRunnable = () -> { + if (!initied) { + return; + } - makeCurrentContext(); + makeCurrentContext(); - if (updateSurface) { - videoSurfaceTexture.updateTexImage(); - videoSurfaceTexture.getTransformMatrix(videoTextureMatrix); - setRenderData(); - updateSurface = false; - filterShaders.onVideoFrameUpdate(videoTextureMatrix); - videoFrameAvailable = true; - } + if (updateSurface) { + videoSurfaceTexture.updateTexImage(); + videoSurfaceTexture.getTransformMatrix(videoTextureMatrix); + setRenderData(); + updateSurface = false; + filterShaders.onVideoFrameUpdate(videoTextureMatrix); + videoFrameAvailable = true; + } - if (!renderDataSet) { - return; + if (!renderDataSet) { + return; + } + + if (isVideo && filterShaders.drawOriginal()) { + GLES20.glViewport(0, 0, surfaceWidth, surfaceHeight); + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + + GLES20.glUseProgram(simpleOESShaderProgram); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, videoTexture[0]); + + GLES20.glUniform1i(simpleOESSourceImageHandle, 0); + GLES20.glEnableVertexAttribArray(simpleOESInputTexCoordHandle); + GLES20.glVertexAttribPointer(simpleOESInputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, textureBuffer != null ? textureBuffer : filterShaders.getTextureBuffer()); + GLES20.glEnableVertexAttribArray(simpleOESPositionHandle); + GLES20.glVertexAttribPointer(simpleOESPositionHandle, 2, GLES20.GL_FLOAT, false, 8, filterShaders.getVertexInvertBuffer()); + GLES20.glUniformMatrix4fv(simpleOESMatrixHandle, 1, false, videoTextureMatrix, 0); + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + egl10.eglSwapBuffers(eglDisplay, eglSurface); + + if (uiBlur != null) { + uiBlur.draw(videoTextureMatrix, videoTexture[0], videoWidth, videoHeight); } - if (videoDelegate == null || videoFrameAvailable) { - GLES20.glViewport(0, 0, renderBufferWidth, renderBufferHeight); - filterShaders.drawSkinSmoothPass(); - filterShaders.drawEnhancePass(); - if (videoDelegate == null) { - filterShaders.drawSharpenPass(); - } - filterShaders.drawCustomParamsPass(); - blurred = filterShaders.drawBlurPass(); - filterTextureAvailable = true; + return; + } + + if (videoDelegate == null || videoFrameAvailable) { + GLES20.glViewport(0, 0, renderBufferWidth, renderBufferHeight); + filterShaders.drawSkinSmoothPass(); + filterShaders.drawEnhancePass(); + if (videoDelegate == null) { + filterShaders.drawSharpenPass(); } + filterShaders.drawCustomParamsPass(); + blurred = filterShaders.drawBlurPass(); + filterTextureAvailable = true; + } - if (filterTextureAvailable) { - GLES20.glViewport(0, 0, surfaceWidth, surfaceHeight); - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + if (filterTextureAvailable) { + GLES20.glViewport(0, 0, surfaceWidth, surfaceHeight); + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + + int tex = filterShaders.getRenderTexture(blurred ? 0 : 1); - 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); + GLES20.glUseProgram(simpleShaderProgram); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex); + + 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); + + if (uiBlur != null) { + uiBlur.draw(null, tex, renderBufferWidth, renderBufferHeight); } } }; @@ -420,6 +516,37 @@ private Bitmap getRenderBufferBitmap() { return bitmap; } + public Bitmap getUiBlurBitmap() { + if (uiBlur == null) { + return null; + } + return uiBlur.getBitmap(); + } + + public void updateUiBlurTransform(Matrix matrix, int w, int h) { + if (uiBlur == null) { + return; + } + uiBlur.updateTransform(matrix, w, h); + requestRender(false); + } + + public void updateUiBlurGradient(int top, int bottom) { + if (uiBlur == null) { + return; + } + postRunnable(() -> { + uiBlur.updateGradient(top, bottom); + }); + } + + public void updateUiBlurManager(BlurringShader.BlurManager manager) { + if (uiBlur == null) { + return; + } + uiBlur.setBlurManager(manager); + } + public Bitmap getTexture() { if (!initied || !isAlive()) { return null; 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 0ec78a9812..637523188c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/FilterShaders.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/FilterShaders.java @@ -325,7 +325,7 @@ public boolean create() { } } - private static final String simpleVertexVideoShaderCode = + public static final String simpleVertexVideoShaderCode = "attribute vec4 position;" + "uniform mat4 videoMatrix;" + "attribute vec4 inputTexCoord;" + @@ -1114,7 +1114,8 @@ public FilterShaders(boolean video, StoryEntry.HDRInfo hdrInfo) { -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, - 1.0f, -1.0f}; + 1.0f, -1.0f + }; ByteBuffer bb = ByteBuffer.allocateDirect(squareCoordinates.length * 4); bb.order(ByteOrder.nativeOrder()); @@ -1126,7 +1127,8 @@ public FilterShaders(boolean video, StoryEntry.HDRInfo hdrInfo) { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, - 1.0f, 1.0f}; + 1.0f, 1.0f + }; bb = ByteBuffer.allocateDirect(squareCoordinates2.length * 4); bb.order(ByteOrder.nativeOrder()); @@ -1152,6 +1154,10 @@ public void setDelegate(FilterShadersDelegate filterShadersDelegate) { delegate = filterShadersDelegate; } + public boolean drawOriginal() { + return delegate == null || delegate.shouldShowOriginal(); + } + public boolean create() { GLES20.glGenTextures(1, curveTextures, 0); GLES20.glGenTextures(2, enhanceTextures, 0); @@ -1975,8 +1981,7 @@ 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 boolean scaleBitmap = true; public void setScaleBitmap(boolean scale) { this.scaleBitmap = scale; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/FloatingDebug/FloatingDebugView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/FloatingDebug/FloatingDebugView.java index 33d8a6cd7c..7b02f18664 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/FloatingDebug/FloatingDebugView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/FloatingDebug/FloatingDebugView.java @@ -50,6 +50,7 @@ import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.INavigationLayout; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.BlurSettingsBottomSheet; import org.telegram.ui.Cells.HeaderCell; import org.telegram.ui.Components.AnimationProperties; import org.telegram.ui.Components.CombinedDrawable; @@ -483,6 +484,18 @@ protected void onConfigurationChanged(Configuration newConfig) { private List getBuiltInDebugItems() { List items = new ArrayList<>(); + + items.add(new FloatingDebugController.DebugItem("Theme")); + items.add(new FloatingDebugController.DebugItem("Draw action bar shadow", () -> { + SharedConfig.drawActionBarShadow = !SharedConfig.drawActionBarShadow; + SharedConfig.saveDebugConfig(); + AndroidUtilities.forEachViews(LaunchActivity.instance.drawerLayoutContainer.getRootView(), View::invalidate); + })); + items.add(new FloatingDebugController.DebugItem("Show blur settings", () -> { + BlurSettingsBottomSheet.show(LaunchActivity.getLastFragment()); + showBigMenu(false); + })); + items.add(new FloatingDebugController.DebugItem(LocaleController.getString(R.string.DebugGeneral))); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { items.add(new FloatingDebugController.DebugItem(LocaleController.getString(SharedConfig.debugWebView ? R.string.DebugMenuDisableWebViewDebug : R.string.DebugMenuEnableWebViewDebug), ()->{ 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 14596e9ed9..975ba0043c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/HintView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/HintView.java @@ -15,6 +15,7 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; +import android.util.Log; import android.util.TypedValue; import android.view.Gravity; import android.view.View; @@ -40,6 +41,7 @@ @SuppressWarnings("FieldCanBeLocal") public class HintView extends FrameLayout { + public static final int TYPE_NOSOUND = 0; public static final int TYPE_SEARCH_AS_LIST = 3; public static final int TYPE_COMMON = 4; public static final int TYPE_POLL_VOTE = 5; @@ -107,11 +109,11 @@ public HintView(Context context, int type, boolean topArrow, Theme.ResourcesProv } else { textView.setGravity(Gravity.LEFT | Gravity.TOP); textView.setBackground(Theme.createRoundRectDrawable(AndroidUtilities.dp(6), getThemedColor(Theme.key_chat_gifSaveHintBackground))); - textView.setPadding(AndroidUtilities.dp(currentType == 0 ? 54 : 8), AndroidUtilities.dp(7), AndroidUtilities.dp(8), AndroidUtilities.dp(8)); + textView.setPadding(AndroidUtilities.dp(currentType == TYPE_NOSOUND ? 54 : 8), AndroidUtilities.dp(7), AndroidUtilities.dp(8), AndroidUtilities.dp(8)); addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 0, topArrow ? 6 : 0, 0, topArrow ? 0 : 6)); } - if (type == 0) { + if (type == TYPE_NOSOUND) { textView.setText(LocaleController.getString("AutoplayVideoInfo", R.string.AutoplayVideoInfo)); imageView = new ImageView(context); @@ -169,7 +171,7 @@ public boolean showForMessageCell(ChatMessageCell cell, boolean animated) { } public boolean showForMessageCell(ChatMessageCell cell, Object object, int x, int y, boolean animated) { - if (currentType == TYPE_POLL_VOTE && y == shownY && messageCell == cell || currentType != TYPE_POLL_VOTE && (currentType == 0 && getTag() != null || messageCell == cell)) { + if (currentType == TYPE_POLL_VOTE && y == shownY && messageCell == cell || currentType != TYPE_POLL_VOTE && (currentType == TYPE_NOSOUND && getTag() != null || messageCell == cell)) { return false; } if (hideRunnable != null) { @@ -185,7 +187,7 @@ public boolean showForMessageCell(ChatMessageCell cell, Object object, int x, in View parentView = (View) cell.getParent(); int centerX; - if (currentType == 0) { + if (currentType == TYPE_NOSOUND) { ImageReceiver imageReceiver = cell.getPhotoImage(); top += imageReceiver.getImageY(); int height = (int) imageReceiver.getImageHeight(); @@ -195,6 +197,7 @@ public boolean showForMessageCell(ChatMessageCell cell, Object object, int x, in return false; } centerX = cell.getNoSoundIconCenterX(); + measure(MeasureSpec.makeMeasureSpec(1000, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(1000, MeasureSpec.AT_MOST)); } else if (currentType == TYPE_POLL_VOTE) { Integer count = (Integer) object; centerX = x; @@ -305,7 +308,7 @@ public boolean showForMessageCell(ChatMessageCell cell, Object object, int x, in public void onAnimationEnd(Animator animation) { animatorSet = null; if (!hasCloseButton) { - AndroidUtilities.runOnUIThread(hideRunnable = () -> hide(), currentType == 0 ? 10000 : 2000); + AndroidUtilities.runOnUIThread(hideRunnable = () -> hide(), currentType == TYPE_NOSOUND ? 10000 : 2000); } } }); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ItemOptions.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ItemOptions.java index 29133a3908..ee98672b9c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ItemOptions.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ItemOptions.java @@ -7,6 +7,7 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.PorterDuff; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.util.TypedValue; @@ -25,8 +26,6 @@ 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.BotWebViewVibrationEffect; import org.telegram.messenger.LocaleController; @@ -225,6 +224,22 @@ public ItemOptions putPremiumLock(Runnable onLockClick) { return this; } + public ItemOptions putCheck() { + if (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_text_check); + lastSubItem.getRightIcon().setColorFilter(0xffffffff, PorterDuff.Mode.MULTIPLY); + lastSubItem.getRightIcon().setScaleX(.85f); + lastSubItem.getRightIcon().setScaleY(.85f); + return this; + } + public ItemOptions addGap() { ActionBarPopupWindow.GapView gap = new ActionBarPopupWindow.GapView(context, resourcesProvider); gap.setTag(R.id.fit_width_tag, 1); @@ -238,14 +253,13 @@ public ItemOptions addSpaceGap() { ((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)); + layout.addView(lastLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, -8, 0, 0)); return this; } @@ -298,6 +312,33 @@ public ItemOptions setDimAlpha(int dimAlpha) { return this; } + private boolean forceTop; + public ItemOptions forceTop(boolean force) { + forceTop = force; + return this; + } + + public ItemOptions setBlurBackground(BlurringShader.BlurManager blurManager, float ox, float oy) { + Drawable baseDrawable = context.getResources().getDrawable(R.drawable.popup_fixed_alert2).mutate(); + if (layout instanceof ActionBarPopupWindow.ActionBarPopupWindowLayout) { + layout.setBackgroundDrawable( + new BlurringShader.StoryBlurDrawer(blurManager, layout, BlurringShader.StoryBlurDrawer.BLUR_TYPE_MENU_BACKGROUND) + .makeDrawable(offsetX + ox + layout.getX(), offsetY + oy + layout.getY(), baseDrawable) + ); + } else { + for (int i = 0; i < layout.getChildCount(); ++i) { + View child = layout.getChildAt(i); + if (child instanceof ActionBarPopupWindow.ActionBarPopupWindowLayout) { + child.setBackgroundDrawable( + new BlurringShader.StoryBlurDrawer(blurManager, child, BlurringShader.StoryBlurDrawer.BLUR_TYPE_MENU_BACKGROUND) + .makeDrawable(offsetX + ox + layout.getX() + child.getX(), offsetY + oy + layout.getY() + child.getY(), baseDrawable) + ); + } + } + } + return this; + } + public int getItemsCount() { if (lastLayout == layout) { return lastLayout.getItemsCount(); @@ -314,6 +355,7 @@ public int getItemsCount() { } } + private float offsetX, offsetY; public ItemOptions show() { if (actionBarPopupWindow != null) { return this; @@ -323,7 +365,7 @@ public ItemOptions show() { return this; } - for (int j = 0; j < layout.getChildCount() - 1; ++j) { + for (int j = 0; j < layout.getChildCount(); ++j) { View child = j == layout.getChildCount() - 1 ? lastLayout : layout.getChildAt(j); if (child instanceof ActionBarPopupWindow.ActionBarPopupWindowLayout) { ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout = (ActionBarPopupWindow.ActionBarPopupWindowLayout) child; @@ -368,74 +410,18 @@ public ItemOptions show() { point[0] = 0; } - 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; + if (dimAlpha > 0) { + View dimViewLocal = dimView = new DimView(context); + 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); } - - 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)); + layout.measure(View.MeasureSpec.makeMeasureSpec(container.getMeasuredWidth(), View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(container.getMeasuredHeight(), View.MeasureSpec.AT_MOST)); actionBarPopupWindow = new ActionBarPopupWindow(layout, LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT) { @Override @@ -474,7 +460,7 @@ public void onDismiss() { } int Y; if (scrimView != null) { - if (y + layout.getMeasuredHeight() + AndroidUtilities.dp(16) > AndroidUtilities.displaySize.y) { + if (forceTop || y + layout.getMeasuredHeight() + AndroidUtilities.dp(16) > AndroidUtilities.displaySize.y) { // put above scrimView y -= scrimView.getMeasuredHeight(); y -= layout.getMeasuredHeight(); @@ -494,12 +480,20 @@ public void onDismiss() { actionBarPopupWindow.showAtLocation( container, 0, - (int) (X + this.translateX), - (int) (Y + this.translateY) + (int) (offsetX = (X + this.translateX)), + (int) (offsetY = (Y + this.translateY)) ); return this; } + public float getOffsetX() { + return offsetX; + } + + public float getOffsetY() { + return offsetY; + } + private void dismissDim(ViewGroup container) { if (dimView == null) { return; @@ -558,4 +552,69 @@ public ItemOptions setViewAdditionalOffsets(int left, int top, int right, int bo viewAdditionalOffsets.set(left, top, right, bottom); return this; } + + public class DimView extends View { + + private final Bitmap cachedBitmap; + private final Paint cachedBitmapPaint; + + private final float clipTop; + private final int dim; + + public DimView(Context context) { + super(context); + + if (scrimView != null && scrimView.getParent() instanceof View) { + clipTop = ((View) scrimView.getParent()).getY() + scrimView.getY(); + } else { + clipTop = 0; + } + dim = ColorUtils.setAlphaComponent(0x00000000, dimAlpha); + + 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; + } + } + + @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(); + } + } + } } 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 b4c1742756..ecb09bb49c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinGroupAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinGroupAlert.java @@ -9,6 +9,7 @@ package org.telegram.ui.Components; import android.content.Context; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.TextUtils; import android.util.TypedValue; @@ -32,9 +33,11 @@ import org.telegram.tgnet.TLRPC; 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.ChatActivity; +import androidx.core.content.ContextCompat; import androidx.core.widget.NestedScrollView; public class JoinGroupAlert extends BottomSheet { @@ -82,6 +85,9 @@ public JoinGroupAlert(final Context context, TLObject obj, String group, BaseFra String title = null, about = null; AvatarDrawable avatarDrawable; + boolean verified = false; + boolean scam = false; + boolean fake = false; int participants_count = 0; BackupImageView avatarImageView = new BackupImageView(context); @@ -103,6 +109,9 @@ public JoinGroupAlert(final Context context, TLObject obj, String group, BaseFra avatarImageView.setImage(ImageLocation.getForPhoto(size, chatInvite.photo), "50_50", avatarDrawable, chatInvite); } about = chatInvite.about; + verified = chatInvite.verified; + fake = chatInvite.fake; + scam = chatInvite.scam; } else if (currentChat != null) { avatarDrawable = new AvatarDrawable(currentChat); title = currentChat.title; @@ -110,21 +119,29 @@ public JoinGroupAlert(final Context context, TLObject obj, String group, BaseFra about = chatFull != null ? chatFull.about : null; participants_count = Math.max(currentChat.participants_count, chatFull != null ? chatFull.participants_count : 0); avatarImageView.setForUserOrChat(currentChat, avatarDrawable, currentChat); + verified = currentChat.verified; + fake = currentChat.fake; + scam = currentChat.scam; } - TextView textView = new TextView(context); - 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, 10, 10, participants_count > 0 ? 0 : 20)); + SimpleTextView simpleTextView = new SimpleTextView(context); + simpleTextView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + simpleTextView.setTextSize(20); + simpleTextView.setTextColor(getThemedColor(Theme.key_dialogTextBlack)); + simpleTextView.setText(title); + simpleTextView.setGravity(Gravity.CENTER); + linearLayout.addView(simpleTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 10, 10, 10, participants_count > 0 ? 0 : 20)); + + if (scam || fake) { + simpleTextView.setRightDrawable(getScamDrawable(scam ? 0 : 1)); + } else if (verified) { + simpleTextView.setRightDrawable(getVerifiedCrossfadeDrawable()); + } final boolean isChannel = chatInvite != null && (chatInvite.channel && !chatInvite.megagroup || ChatObject.isChannelAndNotMegaGroup(chatInvite.chat)) || ChatObject.isChannel(currentChat) && !currentChat.megagroup; boolean hasAbout = !TextUtils.isEmpty(about); - textView = new TextView(context); + TextView textView = new TextView(context); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); textView.setTextColor(getThemedColor(Theme.key_dialogTextGray3)); textView.setSingleLine(true); @@ -316,6 +333,12 @@ public JoinGroupAlert(final Context context, TLObject obj, String group, BaseFra } } + private Drawable getVerifiedCrossfadeDrawable() { + Drawable verifiedDrawable = Theme.dialogs_verifiedDrawable; + Drawable verifiedCheckDrawable = Theme.dialogs_verifiedCheckDrawable; + return new CombinedDrawable(verifiedDrawable, verifiedCheckDrawable); + } + public static void showBulletin(Context context, BaseFragment fragment, boolean isChannel) { if (context == null) { if (fragment != null) { @@ -343,4 +366,10 @@ private CharSequence ellipsize(TextView textView, TLRPC.ChatInvite chatInvite, i } return TextUtils.ellipsize(firstName.trim(), textView.getPaint(), AndroidUtilities.dp(120), TextUtils.TruncateAt.END); } + + private Drawable getScamDrawable(int type) { +// ScamDrawable scamDrawable = new ScamDrawable(11, type); +// scamDrawable.setColor(getThemedColor(Theme.key_avatar_subtitleInProfileBlue)); + return type == 0 ? Theme.dialogs_scamDrawable : Theme.dialogs_fakeDrawable; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActionDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActionDrawable.java index b1ddc0149d..f3e93b2ed3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActionDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActionDrawable.java @@ -16,6 +16,7 @@ import android.view.animation.DecelerateInterpolator; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.R; import org.telegram.ui.ActionBar.Theme; public class MediaActionDrawable extends Drawable { 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 6108e1791e..9a987266d7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActivity.java @@ -86,6 +86,7 @@ public class MediaActivity extends BaseFragment implements SharedMediaLayout.Sha public static final int TYPE_MEDIA = 0; public static final int TYPE_STORIES = 1; + public static final int TYPE_ARCHIVED_CHANNEL_STORIES = 2; private int type; @@ -129,7 +130,13 @@ public MediaActivity(Bundle args, SharedMediaLayout.SharedMediaPreloader sharedM 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); + int defaultTab = SharedMediaLayout.TAB_PHOTOVIDEO; + if (type == TYPE_ARCHIVED_CHANNEL_STORIES) { + defaultTab = SharedMediaLayout.TAB_ARCHIVED_STORIES; + } else if (type == TYPE_STORIES) { + defaultTab = SharedMediaLayout.TAB_STORIES; + } + initialTab = getArguments().getInt("start_from", defaultTab); getNotificationCenter().addObserver(this, NotificationCenter.userInfoDidLoad); getNotificationCenter().addObserver(this, NotificationCenter.currentUserPremiumStatusChanged); getNotificationCenter().addObserver(this, NotificationCenter.storiesEnabledUpdate); @@ -273,7 +280,7 @@ protected void drawList(Canvas blurCanvas, boolean top) { this.fragmentView = fragmentView; ActionBarMenu menu2 = actionBar.createMenu(); - if (type == TYPE_STORIES) { + if (type == TYPE_STORIES || type == TYPE_ARCHIVED_CHANNEL_STORIES) { FrameLayout menu = new FrameLayout(context); actionBar.addView(menu, LayoutHelper.createFrame(56, 56, Gravity.RIGHT | Gravity.BOTTOM)); @@ -609,12 +616,17 @@ protected void invalidateBlur() { @Override protected boolean isStoriesView() { - return type == TYPE_STORIES; + return type == TYPE_STORIES || type == TYPE_ARCHIVED_CHANNEL_STORIES; } @Override protected boolean includeStories() { - return type == TYPE_STORIES; + return type == TYPE_STORIES || type == TYPE_ARCHIVED_CHANNEL_STORIES; + } + + @Override + protected boolean isArchivedOnlyStoriesView() { + return type == TYPE_ARCHIVED_CHANNEL_STORIES; } @Override @@ -637,7 +649,7 @@ protected void showActionMode(boolean show) { if (actionModeAnimation != null) { actionModeAnimation.cancel(); } - if (type == TYPE_STORIES) { + if (type == TYPE_STORIES || type == TYPE_ARCHIVED_CHANNEL_STORIES) { disableScroll(show); } if (show) { @@ -711,7 +723,7 @@ public void onAnimationEnd(Animator animation) { protected void onActionModeSelectedUpdate(SparseArray messageObjects) { final int count = messageObjects.size(); actionModeMessageObjects = messageObjects; - if (type == TYPE_STORIES) { + if (type == TYPE_STORIES || type == TYPE_ARCHIVED_CHANNEL_STORIES) { selectedTextView.cancelAnimation(); selectedTextView.setText(LocaleController.formatPluralString("StoriesSelected", count), !LocaleController.isRTL); if (button != null) { @@ -749,7 +761,7 @@ protected void onTabScroll(boolean scrolling) { sharedMediaLayout.getSearchItem().setTranslationY(0); sharedMediaLayout.photoVideoOptionsItem.setTranslationY(0); - if (type == TYPE_STORIES) { + if (type == TYPE_STORIES || type == TYPE_ARCHIVED_CHANNEL_STORIES) { fragmentView.addView(sharedMediaLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL, 0, 0, 0, 64)); } else { fragmentView.addView(sharedMediaLayout); @@ -773,7 +785,9 @@ protected void onTabScroll(boolean scrolling) { } TLObject avatarObject = null; - if (type == TYPE_STORIES) { + if (type == TYPE_ARCHIVED_CHANNEL_STORIES) { + nameTextView[0].setText(LocaleController.getString("ProfileStoriesArchive")); + } else if (type == TYPE_STORIES) { nameTextView[0].setText(LocaleController.getString("ProfileMyStories")); nameTextView[1].setText(LocaleController.getString("ProfileStoriesArchive")); } else if (DialogObject.isEncryptedDialog(dialogId)) { @@ -1032,6 +1046,9 @@ public void onAnimationEnd(Animator animation) { 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 (i == 1 && type == TYPE_ARCHIVED_CHANNEL_STORIES) { + return; + } if (subtitleShown[i] == show && !firstSubtitleCheck[i]) { return; } @@ -1134,258 +1151,27 @@ 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); + private class StoriesTabsView extends BottomPagerTabs { 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); + super(context, resourcesProvider); } - 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() - ); + public Tab[] createTabs() { + return new Tab[] { + new Tab(0, R.raw.msg_stories_saved, LocaleController.getString("ProfileMyStoriesTab", R.string.ProfileMyStoriesTab)), + new Tab(1, R.raw.msg_stories_archive, LocaleController.getString("ProfileStoriesArchiveTab", R.string.ProfileStoriesArchiveTab)) + }; } + } - @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); + @Override + public int getNavigationBarColor() { + int color = getThemedColor(Theme.key_windowBackgroundWhite); + if (storyViewer != null && storyViewer.attachedToParent()) { + return storyViewer.getNavigationBarColor(color); } + return color; } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Input.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Input.java index 9955f695cd..e4c1c36296 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Input.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Input.java @@ -88,6 +88,9 @@ private void fill(Brush brush, boolean registerUndo, Runnable onDone) { } canFill = false; + if (brush instanceof Brush.Eraser) { + renderView.getPainting().hasBlur = false; + } renderView.getPainting().clearStroke(); pointsCount = 0; realPointsCount = 0; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Painting.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Painting.java index d3197180f1..2a43868fe9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Painting.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Painting.java @@ -16,6 +16,7 @@ import org.telegram.messenger.BotWebViewVibrationEffect; import org.telegram.messenger.DispatchQueue; import org.telegram.messenger.Utilities; +import org.telegram.ui.Components.BlurringShader; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.Size; @@ -58,10 +59,6 @@ public static class PaintingData { private Brush brush; private HashMap brushTextures = new HashMap<>(); private Texture bitmapTexture; - private Texture bluredTexture; - private Bitmap imageBitmap; - private int imageBitmapRotation; - private Bitmap bluredBitmap; private ByteBuffer vertexBuffer; private ByteBuffer textureBuffer; private int reusableFramebuffer; @@ -78,8 +75,19 @@ public static class PaintingData { private float[] projection; private float[] renderProjection; - public Painting(Size sz, Bitmap originalBitmap, int originalRotation) { + private Texture bluredTexture; + private Bitmap imageBitmap; + private int imageBitmapRotation; + private Bitmap bluredBitmap; + + private Texture bitmapBlurTexture; + public boolean hasBlur; + + private final BlurringShader.BlurManager blurManager; + + public Painting(Size sz, Bitmap originalBitmap, int originalRotation, BlurringShader.BlurManager blurManager) { renderState = new RenderState(); + this.blurManager = blurManager; size = sz; imageBitmap = originalBitmap; @@ -147,11 +155,18 @@ private void endSuppressingChanges() { } public void setBitmap(Bitmap bitmap) { - if (bitmapTexture != null) { - return; + if (bitmapTexture == null) { + bitmapTexture = new Texture(bitmap); } + } - bitmapTexture = new Texture(bitmap); + public void setBitmap(Bitmap bitmap, Bitmap blurBitmap) { + if (bitmapTexture == null) { + bitmapTexture = new Texture(bitmap); + } + if (bitmapBlurTexture == null) { + bitmapBlurTexture = new Texture(blurBitmap); + } } private boolean helperShown; @@ -377,7 +392,12 @@ public void commitShape(Shape shape, final int color) { } private Slice commitShapeInternal(Shape shape, int color, RectF bounds) { - Slice undoSlice = registerUndo(bounds); + Brush brush = shape.brush; + if (brush == null) { + brush = this.brush; + } + + Slice undoSlice = registerUndo(bounds, blurManager != null && brush instanceof Brush.Blurer); beginSuppressingChanges(); @@ -386,10 +406,6 @@ private Slice commitShapeInternal(Shape shape, int color, RectF bounds) { GLES20.glViewport(0, 0, (int) size.width, (int) size.height); - Brush brush = shape.brush; - if (brush == null) { - brush = this.brush; - } Shader shader = shaders.get(brush.getShaderName(Brush.PAINT_TYPE_COMPOSITE)); if (shader == null) { return null; @@ -485,56 +501,90 @@ public void commitPath(final Path path, final int color, final boolean registerU } private Slice commitPathInternal(final Path path, final int color, RectF bounds) { - Slice undoSlice = registerUndo(bounds); - - beginSuppressingChanges(); - - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, getReusableFramebuffer()); - GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, getTexture(), 0); - - GLES20.glViewport(0, 0, (int) size.width, (int) size.height); - Brush brush = this.brush; if (path != null) { brush = path.getBrush(); } - Shader shader = shaders.get(brush.getShaderName(Brush.PAINT_TYPE_COMPOSITE)); - if (shader == null) { - return null; + Slice undoSlice; + if (blurManager != null && (brush instanceof Brush.Blurer || brush instanceof Brush.Eraser)) { + undoSlice = registerDoubleUndo(bounds, hasBlur); + hasBlur = brush instanceof Brush.Blurer; + } else { + undoSlice = registerUndo(bounds, false); } - GLES20.glUseProgram(shader.program); + beginSuppressingChanges(); - GLES20.glUniformMatrix4fv(shader.getUniform("mvpMatrix"), 1, false, FloatBuffer.wrap(projection)); - GLES20.glUniform1i(shader.getUniform("texture"), 0); - GLES20.glUniform1i(shader.getUniform("mask"), 1); - Shader.SetColorUniform(shader.getUniform("color"), ColorUtils.setAlphaComponent(color, (int) (Color.alpha(color) * brush.getOverrideAlpha()))); + int count = 1; + if (blurManager != null && (brush instanceof Brush.Blurer || brush instanceof Brush.Eraser)) { + // - eraser erases both from def. texture and blur texture + // - blurer blurs in blur texture and erases anything else in def. texture + count = 2; + } + for (int a = 0; a < count; ++a) { - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTexture()); - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, getReusableFramebuffer()); + int tex = getTexture(); + if (blurManager != null && (brush instanceof Brush.Blurer && a == 0 || brush instanceof Brush.Eraser && a == 1)) { + tex = bitmapBlurTexture != null ? bitmapBlurTexture.texture() : 0; + } + if (a == 1 && brush instanceof Brush.Blurer) { + brush = new Brush.Eraser(); + } + GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, tex, 0); - GLES20.glActiveTexture(GLES20.GL_TEXTURE1); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getPaintTexture()); + GLES20.glViewport(0, 0, (int) size.width, (int) size.height); - if (brush instanceof Brush.Blurer && bluredTexture != null) { - GLES20.glUniform1i(shader.getUniform("blured"), 2); - GLES20.glActiveTexture(GLES20.GL_TEXTURE2); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, bluredTexture.texture()); - } + Shader shader = shaders.get(brush.getShaderName(Brush.PAINT_TYPE_COMPOSITE)); + if (shader == null) { + return null; + } - GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ZERO); + GLES20.glUseProgram(shader.program); - GLES20.glVertexAttribPointer(0, 2, GLES20.GL_FLOAT, false, 8, vertexBuffer); - GLES20.glEnableVertexAttribArray(0); - GLES20.glVertexAttribPointer(1, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); - GLES20.glEnableVertexAttribArray(1); + GLES20.glUniformMatrix4fv(shader.getUniform("mvpMatrix"), 1, false, FloatBuffer.wrap(projection)); + GLES20.glUniform1i(shader.getUniform("texture"), 0); + GLES20.glUniform1i(shader.getUniform("mask"), 1); + Shader.SetColorUniform(shader.getUniform("color"), ColorUtils.setAlphaComponent(color, (int) (Color.alpha(color) * brush.getOverrideAlpha()))); - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + + GLES20.glActiveTexture(GLES20.GL_TEXTURE1); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getPaintTexture()); + + Object lock = null; + if (brush instanceof Brush.Blurer) { + GLES20.glUniform1i(shader.getUniform("blured"), 2); + GLES20.glActiveTexture(GLES20.GL_TEXTURE2); + if (blurManager != null) { + lock = blurManager.getTextureLock(); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, blurManager.getTexture()); + } else { + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, bluredTexture.texture()); + } + } - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTexture()); - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ZERO); + + GLES20.glVertexAttribPointer(0, 2, GLES20.GL_FLOAT, false, 8, vertexBuffer); + GLES20.glEnableVertexAttribArray(0); + GLES20.glVertexAttribPointer(1, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); + GLES20.glEnableVertexAttribArray(1); + + if (lock != null) { + synchronized (lock) { + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + } + } else { + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + } + + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTexture()); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + } GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); @@ -543,7 +593,6 @@ private Slice commitPathInternal(final Path path, final int color, RectF bounds) } endSuppressingChanges(); - renderState.reset(); activePath = null; @@ -600,7 +649,7 @@ private void clearStrokeInternal() { helperApplyAlpha = 0f; } - private Slice registerUndo(RectF rect) { + private Slice registerUndo(RectF rect, boolean blurTex) { if (rect == null) { return null; } @@ -610,12 +659,33 @@ private Slice registerUndo(RectF rect) { return null; } - final Slice slice = new Slice(getPaintingData(rect, true).data, rect, delegate.requestDispatchQueue()); + final Slice slice = new Slice(getPaintingData(rect, true, blurTex, false).data, blurTex ? 1 : 0, rect, delegate.requestDispatchQueue()); delegate.requestUndoStore().registerUndo(UUID.randomUUID(), () -> restoreSlice(slice)); return slice; } + private Slice registerDoubleUndo(RectF rect, boolean hadBlur) { + if (rect == null) { + return null; + } + + boolean intersect = rect.setIntersect(rect, getBounds()); + if (!intersect) { + return null; + } + + final Slice slice1 = new Slice(getPaintingData(rect, true, false, false).data, 0, rect, delegate.requestDispatchQueue()); + final Slice slice2 = new Slice(getPaintingData(rect, true, true, false).data, 1, rect, delegate.requestDispatchQueue()); + delegate.requestUndoStore().registerUndo(UUID.randomUUID(), () -> { + restoreSlice(slice1); + restoreSlice(slice2); + hasBlur = hadBlur; + }); + + return slice1; // ehm... + } + private void restoreSlice(final Slice slice) { renderView.performInContext(() -> { restoreSliceInternal(slice, true); @@ -629,7 +699,11 @@ private void restoreSliceInternal(final Slice slice, boolean forget) { ByteBuffer buffer = slice.getData(); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTexture()); + int tex = getTexture(); + if (slice.getTexture() == 1 && bitmapBlurTexture != null) { + tex = bitmapBlurTexture.texture(); + } + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex); GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, slice.getX(), slice.getY(), slice.getWidth(), slice.getHeight(), GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buffer); if (!isSuppressingChanges() && delegate != null) { delegate.contentChanged(); @@ -649,6 +723,10 @@ public void render() { return; } + if (bitmapBlurTexture != null) { + renderBlur(); + } + if (activePath != null) { renderBlitPath(getPaintTexture(), activePath, 1f - .5f * helperAlpha - .5f * helperApplyAlpha); } else if (activeShape != null) { @@ -662,6 +740,53 @@ public void render() { } } + private void renderBlur() { + if (blurManager == null || bitmapBlurTexture == null) { + return; + } + + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + + Shader shader = shaders.get("videoBlur"); + if (shader == null) { + return; + } + + GLES20.glUseProgram(shader.program); + + GLES20.glUniformMatrix4fv(shader.getUniform("mvpMatrix"), 1, false, FloatBuffer.wrap(renderProjection)); + GLES20.glUniform1f(shader.getUniform("flipy"), 0f); + + GLES20.glUniform1i(shader.getUniform("texture"), 0); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, bitmapBlurTexture.texture()); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + + GLES20.glUniform1i(shader.getUniform("blured"), 1); + GLES20.glActiveTexture(GLES20.GL_TEXTURE1); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, blurManager.getTexture()); + + if (activePath != null && this.brush instanceof Brush.Eraser) { + GLES20.glUniform1f(shader.getUniform("eraser"), 1f); + GLES20.glUniform1i(shader.getUniform("mask"), 2); + GLES20.glActiveTexture(GLES20.GL_TEXTURE2); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getPaintTexture()); + } else { + GLES20.glUniform1f(shader.getUniform("eraser"), 0f); + } + + GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ZERO); + + GLES20.glVertexAttribPointer(0, 2, GLES20.GL_FLOAT, false, 8, vertexBuffer); + GLES20.glEnableVertexAttribArray(0); + GLES20.glVertexAttribPointer(1, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); + GLES20.glEnableVertexAttribArray(1); + + synchronized (blurManager.getTextureLock()) { + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + } + } + private void renderBlitShape(int toTexture, int mask, Shape shape, float alpha) { if (shape == null) { return; @@ -750,10 +875,16 @@ private void renderBlitPath(int mask, Path path, float alpha) { GLES20.glActiveTexture(GLES20.GL_TEXTURE1); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mask); - if (brush instanceof Brush.Blurer && bluredTexture != null) { + Object lock = null; + if (brush instanceof Brush.Blurer) { GLES20.glUniform1i(shader.getUniform("blured"), 2); GLES20.glActiveTexture(GLES20.GL_TEXTURE2); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, bluredTexture.texture()); + if (blurManager != null) { + lock = blurManager.getTextureLock(); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, blurManager.getTexture()); + } else if (bluredTexture != null) { + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, bluredTexture.texture()); + } } GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA); @@ -763,7 +894,13 @@ private void renderBlitPath(int mask, Path path, float alpha) { GLES20.glVertexAttribPointer(1, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); GLES20.glEnableVertexAttribArray(1); - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + if (lock != null) { + synchronized (lock) { + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + } + } else { + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + } Utils.HasGLError(); } @@ -796,6 +933,10 @@ private void renderBlit(int texture, float alpha) { } public PaintingData getPaintingData(RectF rect, boolean undo) { + return getPaintingData(rect, undo, false, false); + } + + public PaintingData getPaintingData(RectF rect, boolean undo, boolean onlyBlur, boolean includeBlur) { int minX = (int) rect.left; int minY = (int) rect.top; int width = (int) rect.width(); @@ -838,7 +979,7 @@ public PaintingData getPaintingData(RectF rect, boolean undo) { GLES20.glUniform1i(shader.getUniform("texture"), 0); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTexture()); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, onlyBlur && bitmapBlurTexture != null ? bitmapBlurTexture.texture() : getTexture()); GLES20.glClearColor(0, 0, 0, 0); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); @@ -852,6 +993,42 @@ public PaintingData getPaintingData(RectF rect, boolean undo) { GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + if (includeBlur && !onlyBlur) { + shader = shaders.get("videoBlur"); + if (shader != null && blurManager != null) { + GLES20.glUseProgram(shader.program); + + GLES20.glUniformMatrix4fv(shader.getUniform("mvpMatrix"), 1, false, FloatBuffer.wrap(finalProjection)); + GLES20.glUniform1f(shader.getUniform("flipy"), 0f); + + GLES20.glUniform1i(shader.getUniform("texture"), 0); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, bitmapBlurTexture.texture()); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + + GLES20.glUniform1i(shader.getUniform("blured"), 1); + GLES20.glActiveTexture(GLES20.GL_TEXTURE1); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, blurManager.getTexture()); + + GLES20.glUniform1f(shader.getUniform("eraser"), 0f); + + GLES20.glUniform1i(shader.getUniform("mask"), 2); + GLES20.glActiveTexture(GLES20.GL_TEXTURE2); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTexture()); + + GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA); + + GLES20.glVertexAttribPointer(0, 2, GLES20.GL_FLOAT, false, 8, vertexBuffer); + GLES20.glEnableVertexAttribArray(0); + GLES20.glVertexAttribPointer(1, 2, GLES20.GL_FLOAT, false, 8, textureBuffer); + GLES20.glEnableVertexAttribArray(1); + + synchronized (blurManager.getTextureLock()) { + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + } + } + } + dataBuffer.limit(width * height * 4); GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, dataBuffer); @@ -880,7 +1057,7 @@ public PaintingData getPaintingData(RectF rect, boolean undo) { public void setBrush(Brush value) { brush = value; - if (value instanceof Brush.Blurer && imageBitmap != null) { + if (value instanceof Brush.Blurer && imageBitmap != null && blurManager == null) { int w = imageBitmap.getWidth(), h = imageBitmap.getHeight(); if (imageBitmapRotation == 90 || imageBitmapRotation == 270 || imageBitmapRotation == -90) { int pH = h; @@ -909,7 +1086,7 @@ public void setBrush(Brush value) { canvas.drawBitmap(imageBitmap, 0, 0, imageBitmapPaint); canvas.restore(); if (renderView != null) { - Bitmap bitmap = renderView.getResultBitmap(); + Bitmap bitmap = renderView.getResultBitmap(false, false); if (bitmap != null) { canvas.scale((float) w / bitmap.getWidth(), (float) h / bitmap.getHeight()); canvas.drawBitmap(bitmap, 0, 0, imageBitmapPaint); @@ -931,8 +1108,8 @@ public boolean isPaused() { public void onPause(final Runnable completionRunnable) { renderView.performInContext(() -> { paused = true; - PaintingData data = getPaintingData(getBounds(), true); - backupSlice = new Slice(data.data, getBounds(), delegate.requestDispatchQueue()); + PaintingData data = getPaintingData(getBounds(), true, false, false); // TODO: save also blur bitmap? + backupSlice = new Slice(data.data, 0, getBounds(), delegate.requestDispatchQueue()); cleanResources(false); @@ -954,7 +1131,12 @@ public void cleanResources(boolean recycle) { reusableFramebuffer = 0; } - bitmapTexture.cleanResources(recycle); + if (bitmapTexture != null) { + bitmapTexture.cleanResources(recycle); + } + if (bitmapBlurTexture != null) { + bitmapBlurTexture.cleanResources(recycle); + } if (paintTexture != 0) { buffers[0] = paintTexture; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/RenderView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/RenderView.java index e9800ad1e2..7b94333e02 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/RenderView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/RenderView.java @@ -17,6 +17,7 @@ import org.telegram.messenger.BuildVars; import org.telegram.messenger.DispatchQueue; import org.telegram.messenger.FileLog; +import org.telegram.ui.Components.BlurringShader; import org.telegram.ui.Components.Size; import java.util.concurrent.CountDownLatch; @@ -47,6 +48,7 @@ default void invalidateInputView() {} private Input input; private ShapeInput shapeInput; private Bitmap bitmap; + private Bitmap blurBitmap; private boolean transformedBitmap; private boolean firstDrawSent; @@ -59,11 +61,12 @@ default void invalidateInputView() {} public boolean isColorPicker = false; - public RenderView(Context context, Painting paint, Bitmap b) { + public RenderView(Context context, Painting paint, Bitmap bitmap, Bitmap blurBitmap, BlurringShader.BlurManager blurManager) { super(context); setOpaque(false); - bitmap = b; + this.bitmap = bitmap; + this.blurBitmap = blurBitmap; painting = paint; painting.setRenderView(this); @@ -73,7 +76,7 @@ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int hei if (surface == null || internal != null) { return; } - internal = new CanvasInternal(surface); + internal = new CanvasInternal(surface, blurManager); internal.setBufferSize(width, height); updateTransform(); @@ -344,8 +347,11 @@ private class CanvasInternal extends DispatchQueue { private long lastRenderCallTime; private Runnable scheduledRunnable; - public CanvasInternal(SurfaceTexture surface) { + private final BlurringShader.BlurManager blurManager; + + public CanvasInternal(SurfaceTexture surface, BlurringShader.BlurManager blurManager) { super("CanvasInternal"); + this.blurManager = blurManager; surfaceTexture = surface; } @@ -410,7 +416,8 @@ private boolean initGL() { } int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE}; - eglContext = egl10.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); + EGLContext parentContext = blurManager != null ? blurManager.getParentContext() : EGL10.EGL_NO_CONTEXT; + eglContext = egl10.eglCreateContext(eglDisplay, eglConfig, parentContext, attrib_list); if (eglContext == null) { if (BuildVars.LOGS_ENABLED) { FileLog.e("eglCreateContext failed " + GLUtils.getEGLErrorString(egl10.eglGetError())); @@ -418,6 +425,10 @@ private boolean initGL() { finish(); return false; } + if (blurManager != null) { + blurManager.acquiredContext(eglContext); + blurManager.attach(safeRequestRender); + } if (surfaceTexture instanceof SurfaceTexture) { eglSurface = egl10.eglCreateWindowSurface(eglDisplay, eglConfig, surfaceTexture, null); @@ -448,7 +459,7 @@ private boolean initGL() { painting.setupShaders(); checkBitmap(); - painting.setBitmap(bitmap); + painting.setBitmap(bitmap, blurBitmap); Utils.HasGLError(); @@ -470,6 +481,13 @@ private void checkBitmap() { bitmap = b; transformedBitmap = true; } + if (blurBitmap != null && (blurBitmap.getWidth() != paintingSize.width || blurBitmap.getHeight() != paintingSize.height)) { + Bitmap b = Bitmap.createBitmap((int) paintingSize.width, (int) paintingSize.height, Bitmap.Config.ALPHA_8); + Canvas canvas = new Canvas(b); + canvas.drawBitmap(blurBitmap, null, new RectF(0, 0, paintingSize.width, paintingSize.height), null); + blurBitmap = b; + transformedBitmap = true; + } } private boolean setCurrentContext() { @@ -522,9 +540,18 @@ public void setBufferSize(int width, int height) { } public void requestRender() { - postRunnable(() -> drawRunnable.run()); + postRunnable(drawRunnable); } + public Runnable safeRequestRender = () -> { + if (scheduledRunnable != null) { + cancelRunnable(scheduledRunnable); + scheduledRunnable = null; + } + cancelRunnable(drawRunnable); + postRunnable(drawRunnable); + }; + public void scheduleRedraw() { if (scheduledRunnable != null) { cancelRunnable(scheduledRunnable); @@ -546,6 +573,9 @@ public void finish() { eglSurface = null; } if (eglContext != null) { + if (blurManager != null) { + blurManager.destroyedContext(eglContext); + } egl10.eglDestroyContext(eglDisplay, eglContext); eglContext = null; } @@ -553,6 +583,9 @@ public void finish() { egl10.eglTerminate(eglDisplay); eglDisplay = null; } + if (blurManager != null) { + blurManager.detach(safeRequestRender); + } } public void shutdown() { @@ -566,6 +599,10 @@ public void shutdown() { } public Bitmap getTexture() { + return getTexture(false, false); + } + + public Bitmap getTexture(final boolean onlyBlur, final boolean includeBlur) { if (!initialized) { return null; } @@ -573,7 +610,7 @@ public Bitmap getTexture() { final Bitmap[] object = new Bitmap[1]; try { postRunnable(() -> { - Painting.PaintingData data = painting.getPaintingData(new RectF(0, 0, painting.getSize().width, painting.getSize().height), false); + Painting.PaintingData data = painting.getPaintingData(new RectF(0, 0, painting.getSize().width, painting.getSize().height), false, onlyBlur, includeBlur); if (data != null) { object[0] = data.bitmap; } @@ -587,11 +624,11 @@ public Bitmap getTexture() { } } - public Bitmap getResultBitmap() { + public Bitmap getResultBitmap(boolean blurTex, boolean includeBlur) { if (brush instanceof Brush.Shape) { shapeInput.stop(); } - return internal != null ? internal.getTexture() : null; + return internal != null ? internal.getTexture(blurTex, includeBlur) : null; } public void performInContext(final Runnable action) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/ShaderSet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/ShaderSet.java index 24fe0a0861..1a19004364 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/ShaderSet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/ShaderSet.java @@ -206,6 +206,26 @@ public class ShaderSet { " gl_FragColor.rgb = (blurColor.rgb * srcAlpha + dst.rgb * dst.a * (1.0 - srcAlpha)) / outAlpha;" + " gl_FragColor.a = outAlpha;" + "}"; + private static final String PAINT_VIDEOBLUR_FSH = + "precision highp float;" + + "varying vec2 varTexcoord;" + + "uniform sampler2D texture;" + + "uniform sampler2D blured;" + + "uniform float eraser;" + + "uniform float flipy;" + + "uniform sampler2D mask;" + + "void main (void) {" + + " vec2 uv = vec2(varTexcoord.x, flipy > 0. ? 1. - varTexcoord.y : varTexcoord.y);" + + " vec4 dst = texture2D(texture, uv, 0.0);" + + " vec4 blurColor = texture2D(blured, uv, 0.0);" + + " gl_FragColor = dst.a <= 0. ? vec4(0.) : vec4(blurColor.rgb, 1.) * dst.a;" + + " if (eraser > 0.) {" + + " vec4 maskColor = texture2D(mask, uv, 0.0);" + + " if (maskColor.a > 0.) {" + + " gl_FragColor.rgba *= (1. - maskColor.a);" + + " }" + + " }" + + "}"; // shapes private static final String PAINT_SHAPE_FSH = @@ -353,6 +373,13 @@ private static Map> createMap() { shader.put(UNIFORMS, new String[]{"mvpMatrix", "texture", "mask", "blured", "color"}); result.put("compositeWithMaskBlurer", Collections.unmodifiableMap(shader)); + shader = new HashMap<>(); + shader.put(VERTEX, PAINT_BLIT_VSH); + shader.put(FRAGMENT, PAINT_VIDEOBLUR_FSH); + shader.put(ATTRIBUTES, new String[]{"inPosition", "inTexcoord"}); + shader.put(UNIFORMS, new String[]{"mvpMatrix", "texture", "blured", "eraser", "mask", "flipy"}); + result.put("videoBlur", Collections.unmodifiableMap(shader)); + // neon shader = new HashMap<>(); shader.put(VERTEX, PAINT_BRUSH_VSH); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Slice.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Slice.java index c2d05b1d7c..e1408685a6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Slice.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Slice.java @@ -15,11 +15,13 @@ import java.util.zip.Inflater; public class Slice { - private RectF bounds; + private final RectF bounds; + private final int texture; private File file; - public Slice(final ByteBuffer data, RectF rect, DispatchQueue queue) { + public Slice(final ByteBuffer data, int tex, RectF rect, DispatchQueue queue) { bounds = rect; + texture = tex; try { File outputDir = ApplicationLoader.applicationContext.getCacheDir(); @@ -122,4 +124,8 @@ public int getHeight() { public RectF getBounds() { return new RectF(bounds); } + + public int getTexture() { + return texture; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Texture.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Texture.java index 39caaf477e..8a04152311 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Texture.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Texture.java @@ -3,6 +3,7 @@ import android.graphics.Bitmap; import android.opengl.GLES20; import android.os.Build; +import android.util.Log; import org.telegram.ui.Components.Size; 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 c42127e7c8..cef38cfc43 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 @@ -32,8 +32,6 @@ public class EditTextOutline extends EditTextBoldCursor { private int mFrameColor; private Path path = new Path(); - public boolean betterFraming; - private RectF[] lines; public RectF framePadding; private boolean isFrameDirty; @@ -160,9 +158,7 @@ protected void onDraw(Canvas canvas) { } if (mFrameColor != 0) { canvas.save(); - if (betterFraming) { - canvas.translate(getPaddingLeft(), getPaddingTop()); - } + canvas.translate(getPaddingLeft(), getPaddingTop()); paint.setColor(mFrameColor); Layout layout = getLayout(); if (layout == null) { @@ -182,16 +178,11 @@ protected void onDraw(Canvas canvas) { lines[i].set(layout.getLineLeft(i), layout.getLineTop(i), layout.getLineRight(i), layout.getLineBottom(i)); 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); - } + 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].left = lines[i].right; } @@ -217,14 +208,7 @@ protected void onDraw(Canvas canvas) { framePadding.bottom = getMeasuredHeight() - framePadding.bottom; } path.rewind(); - float h = getHeight(); - for (int i = 0; i < lines.length; ++i) { - if (lines[i].width() == 0) { - continue; - } - h = lines[i].bottom - lines[i].top; - } - float r = Math.min(h / 3f, dp(16)), mr = r * 1.5f; + float r = getTextSize() / 3f, mr = r * 1.5f; for (int i = 1; i < lines.length; ++i) { RectF above = lines[i - 1]; RectF line = lines[i]; 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 3ec45d7f24..eb9c457341 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 @@ -10,8 +10,6 @@ import android.graphics.DashPathEffect; import android.graphics.Paint; import android.os.Build; -import android.util.Log; -import android.view.GestureDetector; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.View; @@ -66,6 +64,7 @@ default void onEntityDragMultitouchStart() {} default void onEntityDragMultitouchEnd() {} default void onEntityDragEnd(boolean delete) {} default void onEntityDragTrash(boolean enter) {} + default void onEntityHandleTouched() {} } private float previousLocationX, previousLocationY; @@ -142,6 +141,10 @@ protected float getMaxScale() { return 100f; } + protected float getMinScale() { + return 0f; + } + public float getScale() { return getScaleX(); } @@ -231,13 +234,13 @@ private boolean onTouchMove(float x1, float y1, boolean multitouch, float x2, fl return false; } - private void onTouchUp() { + private void onTouchUp(boolean canceled) { if (announcedDrag) { delegate.onEntityDragEnd(announcedTrash); announcedDrag = false; } announcedMultitouchDrag = false; - if (!recognizedLongPress && !hasPanned && !hasTransformed && !announcedSelection && delegate != null) { + if (!canceled && !recognizedLongPress && !hasPanned && !hasTransformed && !announcedSelection && delegate != null) { delegate.onEntitySelected(this); } if (hasPanned && delegate != null) { @@ -382,7 +385,7 @@ public boolean onTouchEvent(MotionEvent event) { // case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { - onTouchUp(); + onTouchUp(action == MotionEvent.ACTION_CANCEL); bounce.setPressed(false); handled = true; if (selectionView != null) { @@ -586,17 +589,22 @@ protected void updatePosition() { public void scale(float scale) { this.scale *= scale; float newScale = Math.max(this.scale, 0.1f); - newScale = Math.min(newScale, getMaxScale()); - if (getScale() < getMaxScale() && newScale >= getMaxScale()) { + newScale = Utilities.clamp(newScale, getMaxScale(), getMinScale()); + if (allowHaptic() && (newScale >= getMaxScale() || newScale <= getMinScale())) { try { performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); - } catch (Exception ignore) {} + } catch (Exception ignore) { + } } setScaleX(newScale); setScaleY(newScale); // updateSelectionView(); } + protected boolean allowHaptic() { + return true; + } + private float angle; public void rotate(float angle) { if (stickyX != STICKY_NONE) { @@ -882,6 +890,9 @@ public boolean onTouchEvent(MotionEvent event) { if (getParent() instanceof EntitiesContainerView) { ((EntitiesContainerView) getParent()).invalidate(); } + if (handle == SELECTION_WHOLE_HANDLE && allowLongPressOnSelected()) { + AndroidUtilities.runOnUIThread(longPressRunnable, ViewConfiguration.getLongPressTimeout()); + } } } break; @@ -895,6 +906,9 @@ public boolean onTouchEvent(MotionEvent event) { float ty = y - previousLocationY; if (hasTransformed || Math.abs(tx) > dp(2) || Math.abs(ty) > dp(2)) { + if (!hasTransformed && delegate != null) { + delegate.onEntityHandleTouched(); + } hasTransformed = true; AndroidUtilities.cancelRunOnUIThread(longPressRunnable); @@ -924,10 +938,9 @@ public boolean onTouchEvent(MotionEvent event) { } break; -// case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { - onTouchUp(); + onTouchUp(action == MotionEvent.ACTION_CANCEL); currentHandle = 0; handled = true; hide(false); @@ -953,6 +966,10 @@ protected float getShowAlpha() { } } + public boolean allowLongPressOnSelected() { + return false; + } + private float trashScale = 1f; private ValueAnimator trashAnimator; private void updateTrash(boolean enter) { 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 ccc6124539..8599f7d0ed 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 @@ -23,6 +23,7 @@ import android.text.SpannableString; import android.text.Spanned; import android.text.TextUtils; +import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; import android.view.Gravity; @@ -83,6 +84,7 @@ 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; @@ -92,9 +94,9 @@ import java.util.Arrays; import java.util.List; -public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPaintView, PaintToolsView.Delegate, EntityView.EntityViewDelegate, PaintTextOptionsView.Delegate, SizeNotifierFrameLayoutPhoto.SizeNotifierFrameLayoutPhotoDelegate, NotificationCenter.NotificationCenterDelegate { - private PaintCancelView cancelButton; - private PaintDoneView doneButton; +public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPaintView, PaintToolsView.Delegate, EntityView.EntityViewDelegate, PaintTextOptionsView.Delegate, SizeNotifierFrameLayout.SizeNotifierFrameLayoutDelegate, NotificationCenter.NotificationCenterDelegate { + public PaintCancelView cancelButton; + public PaintDoneView doneButton; private float offsetTranslationY; private Bitmap bitmapToEdit; @@ -121,10 +123,10 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh private FrameLayout selectionContainerView; private EntitiesContainerView entitiesView; private FrameLayout topLayout; - private FrameLayout bottomLayout; - private FrameLayout overlayLayout; + public FrameLayout bottomLayout; + public FrameLayout overlayLayout; private FrameLayout pipetteContainerLayout; - private LinearLayout tabsLayout; + public LinearLayout tabsLayout; private int tabsSelectedIndex = 0; private int tabsNewSelectedIndex = -1; @@ -133,7 +135,7 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh private boolean ignoreToolChangeAnimationOnce; - private PaintWeightChooserView weightChooserView; + public PaintWeightChooserView weightChooserView; private PaintWeightChooserView.ValueOverride weightDefaultValueOverride = new PaintWeightChooserView.ValueOverride() { @Override public float get() { @@ -161,7 +163,7 @@ public void set(float val) { private TextView textTab; private PaintToolsView paintToolsView; - private PaintTextOptionsView textOptionsView; + public PaintTextOptionsView textOptionsView; private PaintTypefaceListView typefaceListView; private ImageView undoButton; private LinearLayout zoomOutButton; @@ -275,7 +277,7 @@ public LPhotoPaintView(Context context, Activity activity, int currentAccount, B undoAllButton.setClickable(canUndo); }); - renderView = new RenderView(context, new Painting(getPaintingSize(), originalBitmap, originalRotation), bitmapToEdit) { + renderView = new RenderView(context, new Painting(getPaintingSize(), originalBitmap, originalRotation, null), bitmapToEdit, null, null) { @Override public void selectBrush(Brush brush) { int index = 1 + Brush.BRUSHES_LIST.indexOf(brush); @@ -909,6 +911,25 @@ private void setNewColor(int color) { animator.start(); } + private float pany; + public void translateY(float ty) { + if (Math.abs(ty - pany) > 0.1f) { + pany = ty; + setTransform(scale, inputTransformX, inputTransformY, imageWidth, imageHeight); + } + } + + public boolean isCurrentText() { + return currentEntityView instanceof TextPaintView; + } + + public float getSelectedEntityCenterY() { + if (currentEntityView == null) { + return getY() + entitiesView.getTop() + entitiesView.getMeasuredHeight() / 2f; + } + return getY() + entitiesView.getTop() + currentEntityView.getPositionY(); + } + private TextPaintView createText(boolean select) { onTextAdd(); @@ -1375,7 +1396,8 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureChild(bottomLayout, widthMeasureSpec, heightMeasureSpec); measureChild(weightChooserView, widthMeasureSpec, heightMeasureSpec); measureChild(pipetteContainerLayout, widthMeasureSpec, heightMeasureSpec); - measureChild(overlayLayout, widthMeasureSpec, heightMeasureSpec); + int keyboardPad = Math.max(getPKeyboardHeight(), emojiPadding); + measureChild(overlayLayout, widthMeasureSpec, MeasureSpec.makeMeasureSpec(height - keyboardPad, MeasureSpec.EXACTLY)); topLayout.setPadding(topLayout.getPaddingLeft(), AndroidUtilities.dp(12) + AndroidUtilities.statusBarHeight, topLayout.getPaddingRight(), topLayout.getPaddingBottom()); measureChild(topLayout, widthMeasureSpec, heightMeasureSpec); @@ -1399,6 +1421,10 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } } + protected int getPKeyboardHeight() { + return 0; + } + @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); @@ -1549,7 +1575,7 @@ public boolean hasChanges() { @Override public Bitmap getBitmap(ArrayList entities, Bitmap[] thumbBitmap) { - Bitmap bitmap = renderView.getResultBitmap(); + Bitmap bitmap = renderView.getResultBitmap(false, false); lcm = BigInteger.ONE; if (bitmap != null && entitiesView.entitiesCount() > 0) { Canvas canvas; @@ -1846,6 +1872,7 @@ public void setTransform(float scale, float trX, float trY, float imageWidth, fl tx = trX; ty = trY; } + ty += - emojiPadding / 2f; float finalScale = scale * additionlScale; if (Float.isNaN(finalScale)) { finalScale = 1f; @@ -2014,9 +2041,9 @@ private void showColorList(boolean show) { cancelButton.setProgress(toolsTransformProgress); tabsLayout.setTranslationY(AndroidUtilities.dp(32) * toolsTransformProgress); - if (adjustPanLayoutHelper.animationInProgress()) { - moveBottomLayout[0] = false; - } +// if (adjustPanLayoutHelper.animationInProgress()) { +// moveBottomLayout[0] = false; +// } if (moveBottomLayout[0]) { float progress = show ? toolsTransformProgress : 1f - toolsTransformProgress; bottomLayout.setTranslationY(bottomLayoutTranslationY - AndroidUtilities.dp(40) * progress * (show ? 1 : -1)); @@ -2763,8 +2790,8 @@ private static class StickerPosition { /* === emoji keyboard support === */ private EmojiView emojiView; - private boolean emojiViewVisible, emojiViewWasVisible; - private boolean keyboardVisible, isAnimatePopupClosing; + public boolean emojiViewVisible, emojiViewWasVisible; + public boolean keyboardVisible, isAnimatePopupClosing; private int emojiPadding, emojiWasPadding; private boolean translateBottomPanelAfterResize; private int keyboardHeight, keyboardHeightLand; @@ -2795,6 +2822,9 @@ public void onEmojiButtonClick() { if (emojiViewWasVisible && currentEntityView instanceof TextPaintView) { bottomPanelIgnoreOnce = true; } + if (emojiViewVisible) { + onEmojiViewCloseByClick(); + } showEmojiPopup(emojiViewVisible ? 0 : 1); if (emojiViewWasVisible && currentEntityView instanceof TextPaintView) { final EditTextOutline editText = ((TextPaintView) currentEntityView).getEditText(); @@ -2802,6 +2832,10 @@ public void onEmojiButtonClick() { } } + protected void onEmojiViewCloseByClick() { + + } + private void showEmojiPopup(int show) { final boolean ignore = bottomPanelIgnoreOnce; bottomPanelIgnoreOnce = false; @@ -2840,6 +2874,7 @@ private void showEmojiPopup(int show) { emojiPadding = emojiWasPadding = currentHeight; requestLayout(); + updateKeyboard(); ChatActivityEnterViewAnimatedIconView emojiButton = textOptionsView.getEmojiButton(); if (emojiButton != null) { @@ -2849,32 +2884,32 @@ private void showEmojiPopup(int show) { if (!emojiWasVisible) { if (keyboardVisible) { - translateBottomPanelAfterResize = true; - weightChooserView.startPanTransition(AndroidUtilities.displaySize.y, AndroidUtilities.displaySize.y - emojiPadding); +// translateBottomPanelAfterResize = true; +// weightChooserView.startPanTransition(AndroidUtilities.displaySize.y, AndroidUtilities.displaySize.y - emojiPadding); // weightChooserView.updatePanTransition(0, 1); // weightChooserView.stopPanTransition(); } else { ValueAnimator animator = ValueAnimator.ofFloat(emojiPadding, 0); - weightChooserView.startPanTransition(AndroidUtilities.displaySize.y, AndroidUtilities.displaySize.y - emojiPadding); +// weightChooserView.startPanTransition(AndroidUtilities.displaySize.y, AndroidUtilities.displaySize.y - emojiPadding); animator.addUpdateListener(animation -> { float v = (float) animation.getAnimatedValue(); emojiView.setTranslationY(v); - if (!ignore) { - bottomPanelTranslationY(v, 1f - v / emojiPadding); - } +// if (!ignore) { +// bottomPanelTranslationY(v, 1f - v / emojiPadding); +// } }); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { emojiView.setTranslationY(0); - if (!ignore) { - bottomPanelTranslationY(0, 1); - } - weightChooserView.stopPanTransition(); +// if (!ignore) { +// bottomPanelTranslationY(0, 1); +// } +// weightChooserView.stopPanTransition(); } }); - animator.setDuration(AdjustPanLayoutHelper.keyboardDuration); - animator.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); +// animator.setDuration(AdjustPanLayoutHelper.keyboardDuration); +// animator.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); animator.start(); } @@ -2894,15 +2929,14 @@ public void onAnimationEnd(Animator animation) { if (show == 0) { emojiPadding = 0; } - bottomPanelTranslationY(0, 0); - weightChooserView.startPanTransition(AndroidUtilities.displaySize.y - emojiPadding, AndroidUtilities.displaySize.y); - weightChooserView.updatePanTransition(0, 1); - weightChooserView.stopPanTransition(); + updateKeyboard(); +// bottomPanelTranslationY(0, 0); +// weightChooserView.startPanTransition(AndroidUtilities.displaySize.y - emojiPadding, AndroidUtilities.displaySize.y); +// weightChooserView.updatePanTransition(0, 1); +// weightChooserView.stopPanTransition(); requestLayout(); onWindowSizeChanged(); } - - updatePlusEmojiKeyboardButton(); } private void hideEmojiPopup(boolean byBackButton) { @@ -2915,13 +2949,13 @@ private void hideEmojiPopup(boolean byBackButton) { ValueAnimator animator = ValueAnimator.ofFloat(0, height); final boolean ignore = bottomPanelIgnoreOnce; bottomPanelIgnoreOnce = false; - weightChooserView.startPanTransition(AndroidUtilities.displaySize.y - height, AndroidUtilities.displaySize.y); +// weightChooserView.startPanTransition(AndroidUtilities.displaySize.y - height, AndroidUtilities.displaySize.y); animator.addUpdateListener(animation -> { float v = (float) animation.getAnimatedValue(); emojiView.setTranslationY(v); - if (!ignore) { - bottomPanelTranslationY(v - height, 1f - v / height); - } +// if (!ignore) { +// bottomPanelTranslationY(v - height, 1f - v / height); +// } }); isAnimatePopupClosing = true; animator.addListener(new AnimatorListenerAdapter() { @@ -2929,10 +2963,10 @@ private void hideEmojiPopup(boolean byBackButton) { public void onAnimationEnd(Animator animation) { isAnimatePopupClosing = false; emojiView.setTranslationY(0); - if (!ignore) { - bottomPanelTranslationY(0, 0); - } - weightChooserView.stopPanTransition(); +// if (!ignore) { +// bottomPanelTranslationY(0, 0); +// } +// weightChooserView.stopPanTransition(); hideEmojiView(); } }); @@ -2947,12 +2981,12 @@ public void onAnimationEnd(Animator animation) { private boolean bottomPanelIgnoreOnce; private void bottomPanelTranslationY(float ty, float progress) { - bottomLayout.setTranslationY(ty - emojiPadding + progress * AndroidUtilities.dp(40)); - panTranslationY = (ty - emojiPadding) / 2f; // (-keyboardHeight / 2f * (panTranslationProgress) + -(emojiPadding + ty) / 2); - panTranslationProgress = 1f + 2f * panTranslationY / keyboardHeight; - weightChooserView.updatePanTransition(emojiViewVisible ? ty : 0, progress); - setTransform(scale, inputTransformX, inputTransformY, imageWidth, imageHeight); - ((View) getParent()).invalidate(); +// bottomLayout.setTranslationY(ty - emojiPadding + progress * AndroidUtilities.dp(40)); +// panTranslationY = (ty - emojiPadding) / 2f; // (-keyboardHeight / 2f * (panTranslationProgress) + -(emojiPadding + ty) / 2); +// panTranslationProgress = 1f + 2f * panTranslationY / keyboardHeight; +// weightChooserView.updatePanTransition(emojiViewVisible ? ty : 0, progress); +// setTransform(scale, inputTransformX, inputTransformY, imageWidth, imageHeight); +// ((View) getParent()).invalidate(); } @Override @@ -2963,12 +2997,15 @@ public int getEmojiPadding(boolean panned) { if (keyboardVisible && translateBottomPanelAfterResize && !panned) { return 0; } - if (adjustPanLayoutHelper.animationInProgress() && !keyboardVisible) - return keyboardHeight; +// if (adjustPanLayoutHelper.animationInProgress() && !keyboardVisible) +// return keyboardHeight; return emojiPadding; } private void hideEmojiView() { + if (emojiPadding > 0) { + updateKeyboard(); + } if (!emojiViewVisible && emojiView != null && emojiView.getVisibility() != GONE) { emojiView.setVisibility(GONE); } @@ -3006,6 +3043,7 @@ public void onSizeChanged(int height, boolean isWidthGreater) { emojiPadding = emojiWasPadding = layoutParams.height; requestLayout(); + updateKeyboard(); onWindowSizeChanged(); } } @@ -3030,6 +3068,7 @@ public void onSizeChanged(int height, boolean isWidthGreater) { if (emojiPadding != 0 && !keyboardVisible && keyboardVisible != oldValue && !emojiViewVisible) { emojiPadding = 0; requestLayout(); + updateKeyboard(); } if (oldValue && !keyboardVisible && emojiPadding > 0 && translateBottomPanelAfterResize) { translateBottomPanelAfterResize = false; @@ -3041,11 +3080,9 @@ public void onSizeChanged(int height, boolean isWidthGreater) { AndroidUtilities.cancelRunOnUIThread(openKeyboardRunnable); } onWindowSizeChanged(); - - updatePlusEmojiKeyboardButton(); } - private void updatePlusEmojiKeyboardButton() { + public void updatePlusEmojiKeyboardButton() { if (textOptionsView != null) { if (keyboardVisible) { textOptionsView.animatePlusToIcon(R.drawable.input_smile); @@ -3212,55 +3249,19 @@ public void onClearEmojiRecent() { addView(emojiView); } - AdjustPanLayoutHelper adjustPanLayoutHelper = new AdjustPanLayoutHelper(this, false) { - @Override - protected void onTransitionStart(boolean keyboardVisible, int previousHeight, int contentHeight) { - super.onTransitionStart(keyboardVisible, contentHeight); - weightChooserView.startPanTransition(previousHeight, contentHeight); - - if (isColorListShown) { - showColorList(false); - } - } - - @Override - protected void onTransitionEnd() { - panTranslationY = 0; - emojiViewWasVisible = false; - setTransform(scale, inputTransformX, inputTransformY, imageWidth, imageHeight); - super.onTransitionEnd(); - weightChooserView.stopPanTransition(); - } - - @Override - protected void onPanTranslationUpdate(float y, float progress, boolean keyboardVisible) { - topLayout.setTranslationY(y); - panTranslationProgress = 1f - progress; - panTranslationY = y / 2; - bottomLayout.setTranslationY(AndroidUtilities.dp(40) * progress); - weightChooserView.updatePanTransition(y, progress); - setTransform(scale, inputTransformX, inputTransformY, imageWidth, imageHeight); - super.onPanTranslationUpdate(y, progress, keyboardVisible); - ((View) getParent()).invalidate(); - } - - @Override - protected boolean heightAnimationEnabled() { - return !destroyed && !emojiViewVisible; - } - }; - @Override public float adjustPanLayoutHelperProgress() { return panTranslationProgress; } + protected void updateKeyboard() { + + } + @Override protected void onAttachedToWindow() { destroyed = false; super.onAttachedToWindow(); - adjustPanLayoutHelper.setResizableView(this); - adjustPanLayoutHelper.onAttach(); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.customTypefacesLoaded); } @@ -3268,7 +3269,6 @@ protected void onAttachedToWindow() { protected void onDetachedFromWindow() { destroyed = true; super.onDetachedFromWindow(); - adjustPanLayoutHelper.onDetach(); NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.customTypefacesLoaded); } 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 2273e6fda3..a7412ef25c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoFilterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoFilterView.java @@ -13,6 +13,7 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.LinearGradient; +import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; @@ -23,7 +24,6 @@ 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; @@ -37,7 +37,7 @@ import android.widget.LinearLayout; import android.widget.TextView; -import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -52,7 +52,6 @@ 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; @@ -98,6 +97,7 @@ public class PhotoFilterView extends FrameLayout implements FilterShaders.Filter private float grainValue; //0 100 private int blurType; //0 none, 1 radial, 2 linear private float sharpenValue; //0 100 + private boolean filtersEmpty; private CurvesToolValue curvesToolValue; private float blurExcludeSize; private Point blurExcludePoint; @@ -316,7 +316,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { } } - public PhotoFilterView(Context context, VideoEditTextureView videoTextureView, Bitmap bitmap, int rotation, MediaController.SavedFilterState state, PaintingOverlay overlay, int hasFaces, boolean mirror, boolean ownLayout, Theme.ResourcesProvider resourcesProvider) { + public PhotoFilterView(Context context, VideoEditTextureView videoTextureView, Bitmap bitmap, int rotation, MediaController.SavedFilterState state, PaintingOverlay overlay, int hasFaces, boolean mirror, boolean ownLayout, BlurringShader.BlurManager blurManager, Theme.ResourcesProvider resourcesProvider) { super(context); this.ownLayout = ownLayout; this.resourcesProvider = resourcesProvider; @@ -372,6 +372,7 @@ public PhotoFilterView(Context context, VideoEditTextureView videoTextureView, B blurExcludeSize = state.blurExcludeSize; blurExcludePoint = state.blurExcludePoint; blurExcludeBlurSize = state.blurExcludeBlurSize; + filtersEmpty = state.isEmpty(); blurAngle = state.blurAngle; lastState = state; } else { @@ -380,6 +381,7 @@ public PhotoFilterView(Context context, VideoEditTextureView videoTextureView, B blurExcludePoint = new Point(0.5f, 0.5f); blurExcludeBlurSize = 0.15f; blurAngle = (float) Math.PI / 2.0f; + filtersEmpty = true; } bitmapToEdit = bitmap; orientation = rotation; @@ -392,7 +394,15 @@ public PhotoFilterView(Context context, VideoEditTextureView videoTextureView, B }); } else { ownsTextureView = true; - textureView = new TextureView(context); + textureView = new TextureView(context) { + @Override + public void setTransform(@Nullable Matrix transform) { + super.setTransform(transform); + if (eglThread != null) { + eglThread.updateUiBlurTransform(transform, getWidth(), getHeight()); + } + } + }; if (ownLayout) { addView(textureView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); } @@ -401,7 +411,11 @@ public PhotoFilterView(Context context, VideoEditTextureView videoTextureView, B @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { if (eglThread == null && surface != null) { - eglThread = new FilterGLThread(surface, bitmapToEdit, orientation, isMirrored, null, ownLayout); + eglThread = new FilterGLThread(surface, bitmapToEdit, orientation, isMirrored, null, ownLayout, blurManager, width, height); + if (!ownLayout) { + eglThread.updateUiBlurGradient(gradientTop, gradientBottom); + eglThread.updateUiBlurTransform(textureView.getTransform(null), textureView.getWidth(), textureView.getHeight()); + } eglThread.setFilterGLThreadDelegate(PhotoFilterView.this); eglThread.setSurfaceTextureSize(width, height); eglThread.requestRender(true, true, false); @@ -454,6 +468,7 @@ public void onSurfaceTextureUpdated(SurfaceTexture surface) { curvesControl = new PhotoFilterCurvesControl(context, curvesToolValue); curvesControl.setDelegate(() -> { + updateFiltersEmpty(); if (eglThread != null) { eglThread.requestRender(false); } @@ -834,6 +849,26 @@ private void setShowOriginal(boolean value) { } } + private void updateFiltersEmpty() { + filtersEmpty = + 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 && + curvesToolValue.shouldBeSkipped(); + } + public void switchMode() { if (selectedTool == 0) { blurControl.setVisibility(INVISIBLE); @@ -1098,7 +1133,7 @@ public Point getBlurExcludePoint() { @Override public boolean shouldShowOriginal() { - return showOriginal; + return showOriginal || filtersEmpty; } @Override @@ -1138,6 +1173,7 @@ private int getThemedColor(int key) { public void setEnhanceValue(float value) { enhanceValue = value * 100f; + updateFiltersEmpty(); for (int i = 0; i < recyclerListView.getChildCount(); ++i) { View child = recyclerListView.getChildAt(i); if (child instanceof PhotoEditToolCell && recyclerListView.getChildAdapterPosition(child) == enhanceTool) { @@ -1203,6 +1239,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { if (eglThread != null) { eglThread.requestRender(true); } + updateFiltersEmpty(); }); } else { view = new PhotoEditRadioCell(mContext); @@ -1217,6 +1254,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { if (eglThread != null) { eglThread.requestRender(false); } + updateFiltersEmpty(); }); } return new RecyclerListView.Holder(view); @@ -1449,4 +1487,21 @@ protected void onDraw(Canvas canvas) { } } } + + public Bitmap getUiBlurBitmap() { + if (eglThread == null) { + return null; + } + return eglThread.getUiBlurBitmap(); + } + + private int gradientTop, gradientBottom; + public void updateUiBlurGradient(int top, int bottom) { + if (eglThread != null) { + eglThread.updateUiBlurGradient(top, bottom); + } else { + gradientTop = top; + gradientBottom = bottom; + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoPaintView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoPaintView.java deleted file mode 100644 index fe8322be85..0000000000 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoPaintView.java +++ /dev/null @@ -1,1775 +0,0 @@ -package org.telegram.ui.Components; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.os.Looper; -import android.text.TextUtils; -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.WindowManager; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import org.telegram.messenger.AndroidUtilities; -import org.telegram.messenger.Bitmaps; -import org.telegram.messenger.DispatchQueue; -import org.telegram.messenger.FileLoader; -import org.telegram.messenger.FileLog; -import org.telegram.messenger.LocaleController; -import org.telegram.messenger.MediaController; -import org.telegram.messenger.MessageObject; -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.AlertDialog; -import org.telegram.ui.ActionBar.Theme; -import org.telegram.ui.BubbleActivity; -import org.telegram.ui.Components.Paint.Brush; -import org.telegram.ui.Components.Paint.Painting; -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.ColorPicker; -import org.telegram.ui.Components.Paint.Views.EntitiesContainerView; -import org.telegram.ui.Components.Paint.Views.EntityView; -import org.telegram.ui.Components.Paint.Views.StickerView; -import org.telegram.ui.Components.Paint.Views.TextPaintView; -import org.telegram.ui.PhotoViewer; - -import java.math.BigInteger; -import java.util.ArrayList; - -@SuppressLint("NewApi") -public class PhotoPaintView extends FrameLayout implements IPhotoPaintView, EntityView.EntityViewDelegate { - - private Bitmap bitmapToEdit; - private Bitmap facesBitmap; - private UndoStore undoStore; - - int currentBrush; - - private FrameLayout toolsView; - private TextView cancelTextView; - private TextView doneTextView; - - private FrameLayout curtainView; - private RenderView renderView; - private EntitiesContainerView entitiesView; - private FrameLayout dimView; - private FrameLayout textDimView; - private FrameLayout backgroundView; - private FrameLayout selectionContainerView; - private ColorPicker colorPicker; - - private float transformX; - private float transformY; - private float[] temp = new float[2]; - - private ImageView paintButton; - - private EntityView currentEntityView; - - private boolean editingText; - private Point editedTextPosition; - private float editedTextRotation; - private float editedTextScale; - private String initialText; - - private BigInteger lcm; - - private ActionBarPopupWindow popupWindow; - private ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout; - private Rect popupRect; - - private Size paintingSize; - - private float baseScale; - - private int selectedTextType = 2; - - private Animator colorPickerAnimator; - - private DispatchQueue queue; - //private ArrayList faces; - - private boolean ignoreLayout; - - private Swatch brushSwatch; - - private final static int gallery_menu_done = 1; - - private int originalBitmapRotation; - - private boolean inBubbleMode; - - private MediaController.CropState currentCropState; - private final Theme.ResourcesProvider resourcesProvider; - - private float offsetTranslationY; - - public PhotoPaintView(Context context, Bitmap bitmap, Bitmap originalBitmap, int originalRotation, ArrayList entities, MediaController.CropState cropState, Runnable onInit, Theme.ResourcesProvider resourcesProvider) { - super(context); - this.resourcesProvider = resourcesProvider; - - inBubbleMode = context instanceof BubbleActivity; - currentCropState = cropState; - queue = new DispatchQueue("Paint"); - - originalBitmapRotation = originalRotation; - - bitmapToEdit = bitmap; - facesBitmap = originalBitmap; - undoStore = new UndoStore(); - undoStore.setDelegate(() -> colorPicker.setUndoEnabled(undoStore.canUndo())); - - curtainView = new FrameLayout(context); - curtainView.setBackgroundColor(0x22000000); - curtainView.setVisibility(INVISIBLE); - addView(curtainView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); - - renderView = new RenderView(context, new Painting(getPaintingSize(), originalBitmap, originalRotation), bitmap); - renderView.setDelegate(new RenderView.RenderViewDelegate() { - - @Override - public void onFirstDraw() { - onInit.run(); - } - - @Override - public void onBeganDrawing() { - if (currentEntityView != null) { - selectEntity(null); - } - } - - @Override - public void onFinishedDrawing(boolean moved) { - colorPicker.setUndoEnabled(undoStore.canUndo()); - } - - @Override - public boolean shouldDraw() { - boolean draw = currentEntityView == null; - if (!draw) { - selectEntity(null); - } - return draw; - } - - @Override - public void resetBrush() { - - } - }); - renderView.setUndoStore(undoStore); - renderView.setQueue(queue); - renderView.setVisibility(View.INVISIBLE); - renderView.setBrush(Brush.BRUSHES_LIST.get(0)); - addView(renderView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); - - entitiesView = new EntitiesContainerView(context, new EntitiesContainerView.EntitiesContainerViewDelegate() { - @Override - public boolean shouldReceiveTouches() { - return textDimView.getVisibility() != VISIBLE; - } - - @Override - public EntityView onSelectedEntityRequest() { - return currentEntityView; - } - - @Override - public void onEntityDeselect() { - selectEntity(null); - } - }); - addView(entitiesView); - - dimView = new FrameLayout(context); - dimView.setAlpha(0); - dimView.setBackgroundColor(0x66000000); - dimView.setVisibility(GONE); - addView(dimView); - - textDimView = new FrameLayout(context); - textDimView.setAlpha(0); - textDimView.setBackgroundColor(0x66000000); - textDimView.setVisibility(GONE); - textDimView.setOnClickListener(v -> closeTextEnter(true)); - - backgroundView = new FrameLayout(context); -// backgroundView.setBackgroundColor(0x7f000000); - Drawable backgroundDrawable = getResources().getDrawable(R.drawable.gradient_bottom).mutate(); - backgroundDrawable.setColorFilter(new PorterDuffColorFilter(0xff000000, PorterDuff.Mode.MULTIPLY)); - backgroundView.setBackground(backgroundDrawable); - addView(backgroundView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 72, Gravity.FILL_HORIZONTAL | Gravity.BOTTOM)); - - selectionContainerView = new FrameLayout(context) { - @Override - public boolean onTouchEvent(MotionEvent event) { - return false; - } - }; - addView(selectionContainerView); - - colorPicker = new ColorPicker(context, originalBitmap == null); - addView(colorPicker); - colorPicker.setDelegate(new ColorPicker.ColorPickerDelegate() { - @Override - public void onBeganColorPicking() { - if (!(currentEntityView instanceof TextPaintView)) { - setDimVisibility(true); - } - } - - @Override - public void onColorValueChanged() { - setCurrentSwatch(colorPicker.getSwatch(), false); - } - - @Override - public void onFinishedColorPicking() { - setCurrentSwatch(colorPicker.getSwatch(), false); - - if (!(currentEntityView instanceof TextPaintView)) { - setDimVisibility(false); - } - } - - @Override - public void onSettingsPressed() { - if (currentEntityView != null) { - if (currentEntityView instanceof StickerView) { - mirrorSticker(); - } else if (currentEntityView instanceof TextPaintView) { - showTextSettings(); - } - } else { - showBrushSettings(); - } - } - - @Override - public void onUndoPressed() { - undoStore.undo(); - } - - @Override - public boolean onColorPicker() { - renderView.isColorPicker = !renderView.isColorPicker; - return renderView.isColorPicker; - } - }); - - toolsView = new FrameLayout(context); - toolsView.setBackgroundColor(0xff000000); - addView(toolsView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.BOTTOM)); - - cancelTextView = new TextView(context); - cancelTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - cancelTextView.setTextColor(0xffffffff); - cancelTextView.setGravity(Gravity.CENTER); - cancelTextView.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_PICKER_SELECTOR_COLOR, 0)); - cancelTextView.setPadding(AndroidUtilities.dp(20), 0, AndroidUtilities.dp(20), 0); - cancelTextView.setText(LocaleController.getString("Cancel", R.string.Cancel).toUpperCase()); - cancelTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - toolsView.addView(cancelTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); - - doneTextView = new TextView(context); - doneTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - doneTextView.setTextColor(getThemedColor(Theme.key_dialogFloatingButton)); - doneTextView.setGravity(Gravity.CENTER); - doneTextView.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_PICKER_SELECTOR_COLOR, 0)); - doneTextView.setPadding(AndroidUtilities.dp(20), 0, AndroidUtilities.dp(20), 0); - doneTextView.setText(LocaleController.getString("Done", R.string.Done).toUpperCase()); - doneTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - toolsView.addView(doneTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.RIGHT)); - - paintButton = new ImageView(context); - paintButton.setScaleType(ImageView.ScaleType.CENTER); - paintButton.setContentDescription(LocaleController.getString("AccDescrPaint", R.string.AccDescrPaint)); - paintButton.setImageResource(R.drawable.msg_photo_draw); - paintButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); - toolsView.addView(paintButton, LayoutHelper.createFrame(54, LayoutHelper.MATCH_PARENT, Gravity.CENTER, 0, 0, 56, 0)); - paintButton.setOnClickListener(v -> selectEntity(null)); - - ImageView stickerButton = new ImageView(context); - stickerButton.setScaleType(ImageView.ScaleType.CENTER); - stickerButton.setImageResource(R.drawable.msg_sticker); - stickerButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); - toolsView.addView(stickerButton, LayoutHelper.createFrame(54, LayoutHelper.MATCH_PARENT, Gravity.CENTER)); - stickerButton.setOnClickListener(v -> openStickersView()); - - ImageView textButton = new ImageView(context); - textButton.setScaleType(ImageView.ScaleType.CENTER); - textButton.setContentDescription(LocaleController.getString("AccDescrPlaceText", R.string.AccDescrPlaceText)); - textButton.setImageResource(R.drawable.msg_photo_text); - textButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); - toolsView.addView(textButton, LayoutHelper.createFrame(54, LayoutHelper.MATCH_PARENT, Gravity.CENTER, 56, 0, 0, 0)); - textButton.setOnClickListener(v -> createText(true)); - - colorPicker.setUndoEnabled(false); - setCurrentSwatch(colorPicker.getSwatch(), false); - updateSettingsButton(); - - if (entities != null && !entities.isEmpty()) { - for (int a = 0, N = entities.size(); a < N; a++) { - VideoEditedInfo.MediaEntity entity = entities.get(a); - EntityView view; - if (entity.type == 0) { - 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 == 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.setText(entity.text); - Swatch swatch = textPaintView.getSwatch(); - swatch.color = entity.color; - textPaintView.setSwatch(swatch); - view = textPaintView; - } else { - continue; - } - view.setX(entity.x * paintingSize.width - entity.viewWidth * (1 - entity.scale) / 2); - view.setY(entity.y * paintingSize.height - entity.viewHeight * (1 - entity.scale) / 2); - view.setPosition(new Point(view.getX() + entity.viewWidth / 2, view.getY() + entity.viewHeight / 2)); - view.setScaleX(entity.scale); - view.setScaleY(entity.scale); - view.setRotation((float) (-entity.rotation / Math.PI * 180)); - } - } - entitiesView.setVisibility(INVISIBLE); - } - - @Override - public void setOffsetTranslationY(float y, float progress, int keyboardHeight, boolean isPan) { - offsetTranslationY = y; - getColorPicker().setTranslationY(y); - getToolsView().setTranslationY(y); - getColorPickerBackground().setTranslationY(y); - getCurtainView().setTranslationY(y); - } - - @Override - public float getOffsetTranslationY() { - return offsetTranslationY; - } - - @Override - public void onResume() { - renderView.redraw(); - } - - @Override - public boolean onTouch(MotionEvent ev) { - if (currentEntityView != null) { - if (editingText) { - closeTextEnter(true); - } else { - selectEntity(null); - } - } - - float x2 = (ev.getX() - renderView.getTranslationX() - getMeasuredWidth() / 2) / renderView.getScaleX(); - float y2 = (ev.getY() - renderView.getTranslationY() - getMeasuredHeight() / 2 + AndroidUtilities.dp(32)) / renderView.getScaleY(); - float rotation = (float) Math.toRadians(-renderView.getRotation()); - float x = (float) (x2 * Math.cos(rotation) - y2 * Math.sin(rotation)) + renderView.getMeasuredWidth() / 2; - float y = (float) (x2 * Math.sin(rotation) + y2 * Math.cos(rotation)) + renderView.getMeasuredHeight() / 2; - - MotionEvent event = MotionEvent.obtain(0, 0, ev.getActionMasked(), x, y, 0); - renderView.onTouch(event); - event.recycle(); - return true; - } - - private Size getPaintingSize() { - if (paintingSize != null) { - return paintingSize; - } - float width = bitmapToEdit.getWidth(); - float height = bitmapToEdit.getHeight(); - - Size size = new Size(width, height); - int maxSide; - switch (SharedConfig.getDevicePerformanceClass()) { - case SharedConfig.PERFORMANCE_CLASS_LOW: - maxSide = 1280; - break; - default: - case SharedConfig.PERFORMANCE_CLASS_AVERAGE: - maxSide = 2560; - break; - case SharedConfig.PERFORMANCE_CLASS_HIGH: - maxSide = 3840; - break; - } - size.width = maxSide; - size.height = (float) Math.floor(size.width * height / width); - if (size.height > maxSide) { - size.height = maxSide; - size.width = (float) Math.floor(size.height * width / height); - } - paintingSize = size; - return size; - } - - private void updateSettingsButton() { - int resource = R.drawable.photo_paint_brush; - colorPicker.settingsButton.setContentDescription(LocaleController.getString("AccDescrBrushType", R.string.AccDescrBrushType)); - if (currentEntityView != null) { - if (currentEntityView instanceof StickerView) { - resource = R.drawable.msg_photo_flip; - colorPicker.settingsButton.setContentDescription(LocaleController.getString("AccDescrMirror", R.string.AccDescrMirror)); - } else if (currentEntityView instanceof TextPaintView) { - resource = R.drawable.photo_outline; - colorPicker.settingsButton.setContentDescription(LocaleController.getString("PaintOutlined", R.string.PaintOutlined)); - } - paintButton.setImageResource(R.drawable.msg_photo_draw); - paintButton.setColorFilter(null); - } else { - if (brushSwatch != null) { - setCurrentSwatch(brushSwatch, true); - brushSwatch = null; - } - paintButton.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_dialogFloatingButton), PorterDuff.Mode.SRC_IN)); - paintButton.setImageResource(R.drawable.msg_photo_draw); - } - backgroundView.setVisibility(currentEntityView instanceof TextPaintView ? View.INVISIBLE : View.VISIBLE); - - colorPicker.setSettingsButtonImage(resource); - } - - @Override - public void updateColors() { - if (paintButton != null && paintButton.getColorFilter() != null) { - paintButton.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_dialogFloatingButton), PorterDuff.Mode.SRC_IN)); - } - if (doneTextView != null) { - doneTextView.setTextColor(getThemedColor(Theme.key_dialogFloatingButton)); - } - } - - @Override - public void init() { - entitiesView.setVisibility(VISIBLE); - renderView.setVisibility(View.VISIBLE); - if (facesBitmap != null) { - //detectFaces(); - } - } - - @Override - public void shutdown() { - renderView.shutdown(); - entitiesView.setVisibility(GONE); - selectionContainerView.setVisibility(GONE); - - queue.postRunnable(() -> { - Looper looper = Looper.myLooper(); - if (looper != null) { - looper.quit(); - } - }); - } - - public FrameLayout getToolsView() { - return toolsView; - } - - public FrameLayout getColorPickerBackground() { - return backgroundView; - } - - public FrameLayout getCurtainView() { - return curtainView; - } - - @Override - public View getDoneView() { - return doneTextView; - } - - @Override - public View getCancelView() { - return cancelTextView; - } - - public ColorPicker getColorPicker() { - return colorPicker; - } - - @Override - public boolean hasChanges() { - return undoStore.canUndo(); - } - - @Override - public Bitmap getBitmap(ArrayList entities, Bitmap[] thumbBitmap) { - Bitmap bitmap = renderView.getResultBitmap(); - lcm = BigInteger.ONE; - if (bitmap != null && entitiesView.entitiesCount() > 0) { - Canvas canvas = null; - 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(); - if (entities != null) { - VideoEditedInfo.MediaEntity mediaEntity = new VideoEditedInfo.MediaEntity(); - if (entity instanceof TextPaintView) { - mediaEntity.type = 1; - TextPaintView textPaintView = (TextPaintView) entity; - mediaEntity.text = textPaintView.getText().toString(); - int type = textPaintView.getType(); - if (type == 0) { - mediaEntity.subType |= 1; - } else if (type == 2) { - mediaEntity.subType |= 4; - } - mediaEntity.color = textPaintView.getSwatch().color; - mediaEntity.fontSize = textPaintView.getTextSize(); - } else if (entity instanceof StickerView) { - mediaEntity.type = 0; - 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)); - } - skipDrawToBitmap = true; - } - if (stickerView.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() / 2) / (float) entitiesView.getMeasuredWidth(); - mediaEntity.textViewY = (y + v.getHeight() / 2) / (float) entitiesView.getMeasuredHeight(); - mediaEntity.textViewWidth = mediaEntity.viewWidth / (float) entitiesView.getMeasuredWidth(); - mediaEntity.textViewHeight = mediaEntity.viewHeight / (float) entitiesView.getMeasuredHeight(); - mediaEntity.scale = scaleX; - - if (thumbBitmap[0] == null) { - thumbBitmap[0] = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig()); - thumbCanvas = new Canvas(thumbBitmap[0]); - thumbCanvas.drawBitmap(bitmap, 0, 0, null); - } - } - canvas = new Canvas(bitmap); - for (int k = 0; k < 2; k++) { - Canvas currentCanvas = k == 0 ? canvas : thumbCanvas; - if (currentCanvas == null || (k == 0 && skipDrawToBitmap)) { - continue; - } - currentCanvas.save(); - currentCanvas.translate(position.x, position.y); - currentCanvas.scale(v.getScaleX(), v.getScaleY()); - currentCanvas.rotate(v.getRotation()); - currentCanvas.translate(-entity.getWidth() / 2, -entity.getHeight() / 2); - if (v instanceof TextPaintView) { - 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 long getLcm() { - return lcm.longValue(); - } - - public void maybeShowDismissalAlert(PhotoViewer photoViewer, Activity parentActivity, final Runnable okRunnable) { - if (editingText) { - closeTextEnter(false); - return; - } - - if (hasChanges()) { - if (parentActivity == null) { - return; - } - AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity); - 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(); - } - } - - private void setCurrentSwatch(Swatch swatch, boolean updateInterface) { - renderView.setColor(swatch.color); - renderView.setBrushSize(swatch.brushWeight); - - if (updateInterface) { - if (brushSwatch == null && paintButton.getColorFilter() != null) { - brushSwatch = colorPicker.getSwatch(); - } - colorPicker.setSwatch(swatch); - } - - if (currentEntityView instanceof TextPaintView) { - ((TextPaintView) currentEntityView).setSwatch(swatch); - } - } - - private void setDimVisibility(final boolean visible) { - Animator animator; - if (visible) { - dimView.setVisibility(VISIBLE); - animator = ObjectAnimator.ofFloat(dimView, View.ALPHA, 0.0f, 1.0f); - } else { - animator = ObjectAnimator.ofFloat(dimView, View.ALPHA, 1.0f, 0.0f); - } - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (!visible) { - dimView.setVisibility(GONE); - } - } - }); - animator.setDuration(200); - animator.start(); - } - - private void setTextDimVisibility(final boolean visible, EntityView view) { - Animator animator; - - if (visible && view != null) { - ViewGroup parent = (ViewGroup) view.getParent(); - if (textDimView.getParent() != null) { - ((EntitiesContainerView) textDimView.getParent()).removeView(textDimView); - } - parent.addView(textDimView, parent.indexOfChild(view)); - } - - view.setSelectionVisibility(!visible); - - if (visible) { - textDimView.setVisibility(VISIBLE); - animator = ObjectAnimator.ofFloat(textDimView, View.ALPHA, 0.0f, 1.0f); - } else { - animator = ObjectAnimator.ofFloat(textDimView, View.ALPHA, 1.0f, 0.0f); - } - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (!visible) { - textDimView.setVisibility(GONE); - if (textDimView.getParent() != null) { - ((EntitiesContainerView) textDimView.getParent()).removeView(textDimView); - } - } - } - }); - animator.setDuration(200); - animator.start(); - } - - @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(); - 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)); - - 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)); - dimView.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST)); - if (currentEntityView != null) { - currentEntityView.updateSelectionView(); - } - selectionContainerView.measure(MeasureSpec.makeMeasureSpec((int) renderWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec((int) renderHeight, MeasureSpec.EXACTLY)); - colorPicker.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY)); - toolsView.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(48), MeasureSpec.EXACTLY)); - curtainView.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY)); - backgroundView.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(72), MeasureSpec.EXACTLY)); - ignoreLayout = false; - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - int width = right - left; - int height = bottom - top; - - int status = (Build.VERSION.SDK_INT >= 21 && !inBubbleMode ? AndroidUtilities.statusBarHeight : 0); - int actionBarHeight = ActionBar.getCurrentActionBarHeight(); - int actionBarHeight2 = actionBarHeight + status; - - int maxHeight = AndroidUtilities.displaySize.y - actionBarHeight - AndroidUtilities.dp(48); - - int x = (int) Math.ceil((width - renderView.getMeasuredWidth()) / 2); - int y = (height - actionBarHeight2 - AndroidUtilities.dp(48) - renderView.getMeasuredHeight()) / 2 + AndroidUtilities.dp(8) + status; - - renderView.layout(x, y, x + renderView.getMeasuredWidth(), y + renderView.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()); - dimView.layout(0, status, dimView.getMeasuredWidth(), status + dimView.getMeasuredHeight()); - selectionContainerView.layout(x, y, x + selectionContainerView.getMeasuredWidth(), y + selectionContainerView.getMeasuredHeight()); - colorPicker.layout(0, actionBarHeight2, colorPicker.getMeasuredWidth(), actionBarHeight2 + colorPicker.getMeasuredHeight()); - toolsView.layout(0, height - toolsView.getMeasuredHeight(), toolsView.getMeasuredWidth(), height); - curtainView.layout(0, y, curtainView.getMeasuredWidth(), y + curtainView.getMeasuredHeight()); - backgroundView.layout(0, height - AndroidUtilities.dp(45) - backgroundView.getMeasuredHeight(), backgroundView.getMeasuredWidth(), height - AndroidUtilities.dp(45)); - } - - @Override - public void requestLayout() { - if (ignoreLayout) { - return; - } - super.requestLayout(); - } - - @Override - public boolean onEntitySelected(EntityView entityView) { - return selectEntity(entityView); - } - - @Override - public boolean onEntityLongClicked(EntityView entityView) { - showMenuForEntity(entityView); - return true; - } - - @Override - public void getTransformedTouch(float x, float y, float[] output) { - float x2 = (x - AndroidUtilities.displaySize.x / 2); - float y2 = (y - AndroidUtilities.displaySize.y / 2); - float rotation = (float) Math.toRadians(-entitiesView.getRotation()); - output[0] = (float) (x2 * Math.cos(rotation) - y2 * Math.sin(rotation)) + AndroidUtilities.displaySize.x / 2; - output[1] = (float) (x2 * Math.sin(rotation) + y2 * Math.cos(rotation)) + AndroidUtilities.displaySize.y / 2; - } - - @Override - public int[] getCenterLocation(EntityView entityView) { - return getCenterLocationInWindow(entityView); - } - - @Override - public boolean allowInteraction(EntityView entityView) { - return !editingText; - } - - @Override - protected boolean drawChild(Canvas canvas, View child, long drawingTime) { - boolean restore = false; - if ((child == renderView || 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) / 2) + transformX; - float y = (getMeasuredHeight() - actionBarHeight2 - AndroidUtilities.dp(48) - h) / 2 + 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 Point centerPositionForEntity() { - Size paintingSize = getPaintingSize(); - float x = paintingSize.width / 2.0f; - float y = paintingSize.height / 2.0f; - if (currentCropState != null) { - float rotation = (float) Math.toRadians(-(currentCropState.transformRotation + currentCropState.cropRotate)); - float px = (float) (currentCropState.cropPx * Math.cos(rotation) - currentCropState.cropPy * Math.sin(rotation)); - float py = (float) (currentCropState.cropPx * Math.sin(rotation) + currentCropState.cropPy * Math.cos(rotation)); - x -= px * paintingSize.width; - y -= py * paintingSize.height; - } - return new Point(x, y); - } - - 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 + offset, position.y + offset); - } 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) { - occupied = true; - } - } - - if (!occupied) { - break; - } else { - position = new Point(position.x + offset, position.y + offset); - } - } - return position; - } - } - - @Override - public ArrayList 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); - } - } - return result; - } - - @Override - public void setTransform(float scale, float trX, float trY, float imageWidth, float imageHeight) { - transformX = trX; - transformY = trY; - for (int a = 0; a < 3; a++) { - View view; - float additionlScale = 1.0f; - if (a == 0) { - view = entitiesView; - } else { - if (a == 1) { - view = selectionContainerView; - } else { - view = renderView; - } - } - 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 void setOnDoneButtonClickedListener(Runnable callback) { - doneTextView.setOnClickListener(v -> callback.run()); - } - - private boolean selectEntity(EntityView entityView) { - boolean changed = false; - - if (currentEntityView != null) { - if (currentEntityView == entityView) { - if (!editingText) { - showMenuForEntity(currentEntityView); - } - return true; - } else { - currentEntityView.deselect(); - } - 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) { - setCurrentSwatch(((TextPaintView) currentEntityView).getSwatch(), true); - } - - changed = true; - } - - updateSettingsButton(); - - return changed; - } - - private void removeEntity(EntityView entityView) { - if (entityView == currentEntityView) { - currentEntityView.deselect(); - if (editingText) { - closeTextEnter(false); - } - currentEntityView = null; - updateSettingsButton(); - } - entitiesView.removeView(entityView); - undoStore.unregisterUndo(entityView.getUUID()); - } - - private void duplicateSelectedEntity() { - if (currentEntityView == null) { - return; - } - - EntityView entityView = null; - Point position = startPositionRelativeToEntity(currentEntityView); - - if (currentEntityView instanceof StickerView) { - StickerView newStickerView = new StickerView(getContext(), (StickerView) currentEntityView, position); - newStickerView.setDelegate(this); - entitiesView.addView(newStickerView); - entityView = newStickerView; - } else if (currentEntityView instanceof TextPaintView) { - TextPaintView newTextPaintView = new TextPaintView(getContext(), (TextPaintView) currentEntityView, position); - newTextPaintView.setDelegate(this); - newTextPaintView.setMaxWidth((int) (getPaintingSize().width - 20)); - entitiesView.addView(newTextPaintView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); - entityView = newTextPaintView; - } - - registerRemovalUndo(entityView); - selectEntity(entityView); - - updateSettingsButton(); - } - - private void openStickersView() { - StickerMasksAlert stickerMasksAlert = new StickerMasksAlert(getContext(), facesBitmap == null, resourcesProvider); - stickerMasksAlert.setDelegate((parentObject, sticker) -> createSticker(parentObject, sticker, true)); - stickerMasksAlert.setOnDismissListener(dialog -> onOpenCloseStickersAlert(false)); - stickerMasksAlert.show(); - onOpenCloseStickersAlert(true); - } - - protected void onOpenCloseStickersAlert(boolean open) { - - } - - protected void onTextAdd() { - - } - - private Size baseStickerSize() { - float side = (float) Math.floor(getPaintingSize().width * 0.5); - return new Size(side, side); - } - - private void registerRemovalUndo(final EntityView entityView) { - undoStore.registerUndo(entityView.getUUID(), () -> removeEntity(entityView)); - } - - private StickerView createSticker(Object parentObject, TLRPC.Document sticker, boolean select) { - StickerPosition position = calculateStickerPosition(sticker); - StickerView view = new StickerView(getContext(), position.position, position.angle, position.scale, baseStickerSize(), sticker, parentObject) { - @Override - protected void didSetAnimatedSticker(RLottieDrawable drawable) { - PhotoPaintView.this.didSetAnimatedSticker(drawable); - } - }; - view.setDelegate(this); - entitiesView.addView(view); - if (select) { - registerRemovalUndo(view); - selectEntity(view); - } - return view; - } - - protected void didSetAnimatedSticker(RLottieDrawable drawable) { - - } - - private void mirrorSticker() { - if (currentEntityView instanceof StickerView) { - ((StickerView) currentEntityView).mirror(); - } - } - - private TextPaintView createText(boolean select) { - onTextAdd(); - Swatch currentSwatch = colorPicker.getSwatch(); - Swatch swatch; - if (selectedTextType == 0) { - swatch = new Swatch(Color.BLACK, 0.85f, currentSwatch.brushWeight); - } else if (selectedTextType == 1) { - swatch = new Swatch(Color.WHITE, 1.0f, currentSwatch.brushWeight); - } else { - swatch = new Swatch(Color.WHITE, 1.0f, currentSwatch.brushWeight); - } - - Size paintingSize = getPaintingSize(); - TextPaintView view = new TextPaintView(getContext(), startPositionRelativeToEntity(null), (int) (paintingSize.width / 9), "", swatch, selectedTextType); - view.setDelegate(this); - view.setMaxWidth((int) (paintingSize.width - 20)); - 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); - selectEntity(view); - editSelectedTextEntity(); - } - setCurrentSwatch(swatch, true); - return view; - } - - private void editSelectedTextEntity() { - if (!(currentEntityView instanceof TextPaintView) || editingText) { - return; - } - - curtainView.setVisibility(View.VISIBLE); - - final TextPaintView textPaintView = (TextPaintView) currentEntityView; - initialText = textPaintView.getText().toString(); - editingText = true; - - editedTextPosition = textPaintView.getPosition(); - editedTextRotation = textPaintView.getRotation(); - editedTextScale = textPaintView.getScale(); - - textPaintView.setPosition(centerPositionForEntity()); - if (currentCropState != null) { - textPaintView.setRotation(-(currentCropState.transformRotation + currentCropState.cropRotate)); - textPaintView.setScale(1.0f / currentCropState.cropScale); - } else { - textPaintView.setRotation(0.0f); - textPaintView.setScale(1.0f); - } - - toolsView.setVisibility(GONE); - - setTextDimVisibility(true, textPaintView); - textPaintView.beginEditing(); - View view = textPaintView.getFocusedView(); - view.requestFocus(); - AndroidUtilities.showKeyboard(view); - } - - public void closeTextEnter(boolean apply) { - if (!editingText || !(currentEntityView instanceof TextPaintView)) { - return; - } - - TextPaintView textPaintView = (TextPaintView) currentEntityView; - - toolsView.setVisibility(VISIBLE); - - AndroidUtilities.hideKeyboard(textPaintView.getFocusedView()); - - textPaintView.getFocusedView().clearFocus(); - textPaintView.endEditing(); - - if (!apply) { - textPaintView.setText(initialText); - } - - if (textPaintView.getText().toString().trim().length() == 0) { - entitiesView.removeView(textPaintView); - selectEntity(null); - } else { - textPaintView.setPosition(editedTextPosition); - textPaintView.setRotation(editedTextRotation); - textPaintView.setScale(editedTextScale); - - editedTextPosition = null; - editedTextRotation = 0.0f; - editedTextScale = 0.0f; - } - - setTextDimVisibility(false, textPaintView); - - editingText = false; - initialText = null; - - curtainView.setVisibility(View.GONE); - } - - private void setBrush(int brush) { - renderView.setBrush(Brush.BRUSHES_LIST.get(currentBrush = brush)); - } - - private void setType(int type) { - selectedTextType = type; - if (currentEntityView instanceof TextPaintView) { - Swatch currentSwatch = colorPicker.getSwatch(); - if (type == 0 && currentSwatch.color == Color.WHITE) { - Swatch blackSwatch = new Swatch(Color.BLACK, 0.85f, currentSwatch.brushWeight); - setCurrentSwatch(blackSwatch, true); - } else if ((type == 1 || type == 2) && currentSwatch.color == Color.BLACK) { - Swatch blackSwatch = new Swatch(Color.WHITE, 1.0f, currentSwatch.brushWeight); - setCurrentSwatch(blackSwatch, true); - } - ((TextPaintView) currentEntityView).setType(type); - } - } - - 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] += px / 2; - pos[1] += py / 2; - return pos; - } - - @Override - public float getCropRotation() { - return currentCropState != null ? currentCropState.cropRotate + currentCropState.transformRotation : 0; - } - - 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.setBackgroundDrawable(Theme.getSelectorDrawable(false)); - deleteView.setGravity(Gravity.CENTER_VERTICAL); - deleteView.setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(14), 0); - deleteView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); - 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.setBackgroundDrawable(Theme.getSelectorDrawable(false)); - editView.setGravity(Gravity.CENTER_VERTICAL); - editView.setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(16), 0); - editView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); - editView.setTag(1); - editView.setText(LocaleController.getString("PaintEdit", R.string.PaintEdit)); - editView.setOnClickListener(v -> { - editSelectedTextEntity(); - - if (popupWindow != null && popupWindow.isShowing()) { - popupWindow.dismiss(true); - } - }); - parent.addView(editView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 48)); - } - - TextView duplicateView = new TextView(getContext()); - duplicateView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); - duplicateView.setBackgroundDrawable(Theme.getSelectorDrawable(false)); - duplicateView.setGravity(Gravity.CENTER_VERTICAL); - duplicateView.setPadding(AndroidUtilities.dp(14), 0, AndroidUtilities.dp(16), 0); - duplicateView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); - duplicateView.setTag(2); - duplicateView.setText(LocaleController.getString("PaintDuplicate", R.string.PaintDuplicate)); - duplicateView.setOnClickListener(v -> { - duplicateSelectedEntity(); - - 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); - } - - @Override - public float adjustPanLayoutHelperProgress() { - return 0; - } - - @Override - public int getEmojiPadding(boolean panned) { - return 0; - } - - @Override - public RenderView getRenderView() { - return renderView; - } - - private LinearLayout buttonForBrush(final int brush, int icon, String text, boolean selected) { - LinearLayout button = new LinearLayout(getContext()) { - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - return true; - } - }; - button.setOrientation(LinearLayout.HORIZONTAL); - button.setBackgroundDrawable(Theme.getSelectorDrawable(false)); - button.setOnClickListener(v -> { - setBrush(brush); - - if (popupWindow != null && popupWindow.isShowing()) { - popupWindow.dismiss(true); - } - }); - - ImageView imageView = new ImageView(getContext()); - imageView.setScaleType(ImageView.ScaleType.CENTER); - imageView.setImageResource(icon); - imageView.setColorFilter(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); - button.addView(imageView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 16, 0, 16, 0)); - - TextView textView = new TextView(getContext()); - textView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); - textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); - textView.setText(text); - textView.setMinWidth(AndroidUtilities.dp(70)); - button.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 0, 0, 16, 0)); - - ImageView check = new ImageView(getContext()); - check.setImageResource(R.drawable.msg_text_check); - check.setScaleType(ImageView.ScaleType.CENTER); - check.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_radioBackgroundChecked), PorterDuff.Mode.SRC_IN)); - check.setVisibility(selected ? VISIBLE : INVISIBLE); - button.addView(check, LayoutHelper.createLinear(50, LayoutHelper.MATCH_PARENT)); - - return button; - } - - private void showBrushSettings() { - showPopup(() -> { - View radial = buttonForBrush(0, R.drawable.msg_draw_pen, LocaleController.getString("PaintPen", R.string.PaintPen), currentBrush == 0); - popupLayout.addView(radial, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 54)); - - View elliptical = buttonForBrush(1, R.drawable.msg_draw_marker, LocaleController.getString("PaintMarker", R.string.PaintMarker), currentBrush == 1); - popupLayout.addView(elliptical, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 54)); - - View neon = buttonForBrush(2, R.drawable.msg_draw_neon, LocaleController.getString("PaintNeon", R.string.PaintNeon), currentBrush == 2); - popupLayout.addView(neon, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 54)); - - View arrow = buttonForBrush(3, R.drawable.msg_draw_arrow, LocaleController.getString("PaintArrow", R.string.PaintArrow), currentBrush == 3); - popupLayout.addView(arrow, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 54)); - - }, this, Gravity.RIGHT | Gravity.BOTTOM, 0, AndroidUtilities.dp(48)); - } - - private LinearLayout buttonForText(int type, String text, int icon, boolean selected) { - LinearLayout button = new LinearLayout(getContext()) { - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - return true; - } - }; - button.setOrientation(LinearLayout.HORIZONTAL); - button.setBackgroundDrawable(Theme.getSelectorDrawable(false)); - button.setOnClickListener(v -> { - setType(type); - - if (popupWindow != null && popupWindow.isShowing()) { - popupWindow.dismiss(true); - } - }); - - ImageView imageView = new ImageView(getContext()); - imageView.setScaleType(ImageView.ScaleType.CENTER); - imageView.setImageResource(icon); - imageView.setColorFilter(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); - button.addView(imageView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 16, 0, 16, 0)); - - TextView textView = new TextView(getContext()); - textView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); - textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); - textView.setText(text); - button.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 0, 0, 16, 0)); - - if (selected) { - ImageView check = new ImageView(getContext()); - check.setImageResource(R.drawable.msg_text_check); - check.setScaleType(ImageView.ScaleType.CENTER); - check.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_radioBackgroundChecked), PorterDuff.Mode.SRC_IN)); - button.addView(check, LayoutHelper.createLinear(50, LayoutHelper.MATCH_PARENT)); - } - - return button; - } - - private void showTextSettings() { - 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; - } - popupLayout.addView(buttonForText(a, text, icon, selectedTextType == a), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); - } - }, this, Gravity.RIGHT | Gravity.BOTTOM, 0, AndroidUtilities.dp(48)); - } - - 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()); - popupLayout.setAnimationEnabled(false); - 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(false); - 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(); - } - - /* - 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); - } - } - PhotoPaintView.this.faces = result; - } catch (Exception e) { - FileLog.e(e); - } finally { - if (faceDetector != null) { - faceDetector.release(); - } - } - }); - } - */ - - private 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; - } - StickerPosition defaultPosition = new 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 + xCompX + yCompX; - float y = referencePoint.y + xCompY + yCompY; - - return new 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; - } - */ - - private int getThemedColor(int key) { - return Theme.getColor(key, resourcesProvider); - } - - 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; - } - } - - @Override - 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 e140da6272..0b4dd7c332 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java @@ -65,7 +65,7 @@ import tw.nekomimi.nekogram.utils.VibrateUtil; -public class PhotoViewerCaptionEnterView extends FrameLayout implements NotificationCenter.NotificationCenterDelegate, SizeNotifierFrameLayoutPhoto.SizeNotifierFrameLayoutPhotoDelegate { +public class PhotoViewerCaptionEnterView extends FrameLayout implements NotificationCenter.NotificationCenterDelegate, SizeNotifierFrameLayout.SizeNotifierFrameLayoutDelegate { private final ImageView doneButton; 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 501ca23708..d127a32629 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PipVideoOverlay.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PipVideoOverlay.java @@ -331,10 +331,14 @@ public static void dismiss() { } public static void dismiss(boolean animate) { - instance.dismissInternal(animate); + instance.dismissInternal(animate, false); } - private void dismissInternal(boolean animate) { + public static void dismiss(boolean animate, boolean immediate) { + instance.dismissInternal(animate, immediate); + } + + private void dismissInternal(boolean animate, boolean immediate) { if (isDismissing) { return; } @@ -356,7 +360,11 @@ private void dismissInternal(boolean animate) { // Animate is a flag for PhotoViewer transition, not ours if (animate || contentView == null) { - AndroidUtilities.runOnUIThread(this::onDismissedInternal, 100); + if (immediate) { + onDismissedInternal(); + } else { + AndroidUtilities.runOnUIThread(this::onDismissedInternal, 100); + } } else { AnimatorSet set = new AnimatorSet(); set.setDuration(250); 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 df01940b9b..9295aab8d3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PollVotesAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PollVotesAlert.java @@ -264,6 +264,7 @@ public class UserCell extends FrameLayout { private SimpleTextView nameTextView; private AvatarDrawable avatarDrawable; + private StatusBadgeComponent statusBadgeComponent; private TLRPC.User currentUser; private TLRPC.Chat currentChat; @@ -297,13 +298,14 @@ public UserCell(Context context) { nameTextView.setTextSize(16); nameTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); 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)); + statusBadgeComponent = new StatusBadgeComponent(nameTextView); } 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){ + } else if (object instanceof TLRPC.Chat) { currentChat = (TLRPC.Chat) object; currentUser = null; } else { @@ -345,6 +347,18 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(48) + (needDivider ? 1 : 0), MeasureSpec.EXACTLY)); } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + statusBadgeComponent.onAttachedToWindow(); + } + + @Override + protected void onDetachedFromWindow() { + statusBadgeComponent.onDetachedFromWindow(); + super.onDetachedFromWindow(); + } + public void update(int mask) { TLRPC.FileLocation photo = null; String newName = null; @@ -396,15 +410,15 @@ public void update(int mask) { avatarDrawable.setInfo(currentChat); } - if (currentUser != null) { lastName = newName == null ? UserObject.getUserName(currentUser) : newName; - } else if (currentChat != null){ + } else if (currentChat != null) { lastName = currentChat.title; } else { lastName = ""; } nameTextView.setText(lastName); + nameTextView.setRightDrawable(statusBadgeComponent.updateDrawable(currentUser, currentChat, Theme.getColor(Theme.key_chats_verifiedBackground), false)); lastAvatar = photo; if (currentChat != null) { 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 3d78fc3b19..21620c7346 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 @@ -64,6 +64,12 @@ public class LimitPreviewView extends LinearLayout { boolean animationCanPlay = true; FrameLayout limitsContainer; private boolean premiumLocked; + private final TextView defaultText; + private final TextView premiumText; + private boolean isBoostsStyle; + + Theme.ResourcesProvider resourcesProvider; + public LimitPreviewView(@NonNull Context context, int icon, int currentValue, int premiumLimit, Theme.ResourcesProvider resourcesProvider) { this(context, icon, currentValue, premiumLimit, .5f, resourcesProvider); @@ -72,6 +78,7 @@ public LimitPreviewView(@NonNull Context context, int icon, int currentValue, in @SuppressLint("SetTextI18n") public LimitPreviewView(@NonNull Context context, int icon, int currentValue, int premiumLimit, float inputPercent, Theme.ResourcesProvider resourcesProvider) { super(context); + this.resourcesProvider = resourcesProvider; final float percent = MathUtils.clamp(inputPercent, 0.1f, 0.9f); this.icon = icon; setOrientation(VERTICAL); @@ -89,7 +96,7 @@ public LimitPreviewView(@NonNull Context context, int icon, int currentValue, in final FrameLayout defaultLayout = new FrameLayout(context); - final TextView defaultText = new TextView(context); + defaultText = new TextView(context); defaultText.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); defaultText.setText(LocaleController.getString("LimitFree", R.string.LimitFree)); defaultText.setGravity(Gravity.CENTER_VERTICAL); @@ -111,7 +118,7 @@ public LimitPreviewView(@NonNull Context context, int icon, int currentValue, in final FrameLayout premiumLayout = new FrameLayout(context); - final TextView premiumText = new TextView(context); + premiumText = new TextView(context); premiumText.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); premiumText.setText(LocaleController.getString("LimitPremium", R.string.LimitPremium)); premiumText.setGravity(Gravity.CENTER_VERTICAL); @@ -137,12 +144,18 @@ public LimitPreviewView(@NonNull Context context, int icon, int currentValue, in @Override protected void dispatchDraw(Canvas canvas) { - grayPaint.setColor(Theme.getColor(Theme.key_windowBackgroundGray, resourcesProvider)); + if (isBoostsStyle) { + grayPaint.setColor(Theme.getColor(Theme.key_graySection, resourcesProvider)); + } else { + grayPaint.setColor(Theme.getColor(Theme.key_windowBackgroundGray, resourcesProvider)); + } AndroidUtilities.rectTmp.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(6), dp(6), grayPaint); canvas.save(); - canvas.clipRect(width1, 0, getMeasuredWidth(), getMeasuredHeight()); + if (!isBoostsStyle) { + canvas.clipRect(width1, 0, getMeasuredWidth(), getMeasuredHeight()); + } Paint paint = PremiumGradient.getInstance().getMainGradientPaint(); if (parentVideForGradient != null) { View parent = parentVideForGradient; @@ -162,6 +175,9 @@ protected void dispatchDraw(Canvas canvas) { } else { PremiumGradient.getInstance().updateMainGradientMatrix(0, 0, LimitPreviewView.this.getMeasuredWidth(), LimitPreviewView.this.getMeasuredHeight(), getGlobalXOffset() - getLeft(), -getTop()); } + if (isBoostsStyle) { + AndroidUtilities.rectTmp.set(0, 0, width1, getMeasuredHeight()); + } canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(6), dp(6), paint); canvas.restore(); if (staticGradient == null) { 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 bd8a780c9d..95adcbaca9 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 @@ -4,6 +4,8 @@ import android.content.Context; import android.content.DialogInterface; import android.graphics.Canvas; +import android.text.SpannableStringBuilder; +import android.text.TextUtils; import android.util.TypedValue; import android.view.Gravity; import android.view.HapticFeedbackConstants; @@ -14,6 +16,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.core.graphics.ColorUtils; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -36,12 +39,12 @@ import org.telegram.ui.Cells.ShadowSectionCell; import org.telegram.ui.Components.BottomSheetWithRecyclerListView; import org.telegram.ui.Components.BulletinFactory; +import org.telegram.ui.Components.ColoredImageSpan; 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.PremiumPreviewFragment; -import org.telegram.ui.Stories.StoriesController; import java.util.ArrayList; import java.util.HashSet; @@ -67,8 +70,11 @@ public class LimitReachedBottomSheet extends BottomSheetWithRecyclerListView { public static final int TYPE_STORIES_COUNT = 14; public static final int TYPE_STORIES_WEEK = 15; public static final int TYPE_STORIES_MONTH = 16; + public static final int TYPE_BOOSTS = 17; private boolean canSendLink; + private int linkRow = -1; + private long dialogId; public static String limitTypeToServerString(int type) { switch (type) { @@ -130,9 +136,9 @@ public static String limitTypeToServerString(int type) { private boolean isVeryLargeFile; private TLRPC.Chat fromChat; - public LimitReachedBottomSheet(BaseFragment fragment, Context context, int type, int currentAccount) { - super(fragment, false, hasFixedSize(type)); - fixNavigationBar(Theme.getColor(Theme.key_dialogBackground, resourcesProvider)); + public LimitReachedBottomSheet(BaseFragment fragment, Context context, int type, int currentAccount, Theme.ResourcesProvider resourcesProvider) { + super(fragment, false, hasFixedSize(type), false, resourcesProvider); + fixNavigationBar(Theme.getColor(Theme.key_dialogBackground, this.resourcesProvider)); this.parentFragment = fragment; this.currentAccount = currentAccount; this.type = type; @@ -279,7 +285,7 @@ private void sendInviteMessages() { public void updatePremiumButtonText() { if (UserConfig.getInstance(currentAccount).isPremium() || MessagesController.getInstance(currentAccount).premiumLocked || isVeryLargeFile) { - premiumButtonView.buttonTextView.setText(LocaleController.getString(R.string.OK)); + premiumButtonView.buttonTextView.setText(LocaleController.getString("OK", R.string.OK)); premiumButtonView.hideIcon(); } else { premiumButtonView.buttonTextView.setText(LocaleController.getString("IncreaseLimit", R.string.IncreaseLimit)); @@ -360,7 +366,10 @@ 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 || type == TYPE_FOLDER_INVITES || type == TYPE_SHARED_FOLDERS || type == TYPE_STORIES_COUNT || type == TYPE_STORIES_WEEK || type == TYPE_STORIES_MONTH) { + 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 || type == TYPE_STORIES_COUNT || type == TYPE_STORIES_WEEK || + type == TYPE_STORIES_MONTH) { return true; } return false; @@ -391,6 +400,21 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int View view; Context context = parent.getContext(); switch (viewType) { + case 7: + FrameLayout frameLayout = new FrameLayout(getContext()); + TextView linkView = new TextView(context); + 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); + frameLayout.addView(linkView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 11, 0, 11, 0)); + linkView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(8), Theme.getColor(Theme.key_graySection, resourcesProvider), ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_listSelector, resourcesProvider), (int) (255 * 0.3f)))); + linkView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + + linkView.setText(getBoostLink()); + linkView.setGravity(Gravity.CENTER); + view = frameLayout; + break; default: case 0: view = new HeaderView(context); @@ -490,6 +514,8 @@ public int getItemViewType(int position) { return 5; } else if (emptyViewDividerRow == position) { return 6; + } else if (linkRow == position) { + return 7; } if (type == TYPE_TO0_MANY_COMMUNITIES || type == TYPE_ADD_MEMBERS_RESTRICTED) { return 4; @@ -505,6 +531,11 @@ public int getItemCount() { }; } + private String getBoostLink() { + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-dialogId); + return "https://" + ChatObject.getPublicUsername(chat) +"?boost"; + } + public void setCurrentValue(int currentValue) { this.currentValue = currentValue; } @@ -526,6 +557,10 @@ public void setRestrictedUsers(TLRPC.Chat chat, ArrayList userRestri updateButton(); } + public void setDialogId(long dialogId) { + this.dialogId = dialogId; + } + private class HeaderView extends LinearLayout { @SuppressLint("SetTextI18n") @@ -810,6 +845,7 @@ private void updateRows() { chatStartRow = -1; chatEndRow = -1; loadingRow = -1; + linkRow = -1; emptyViewDividerRow = -1; headerRow = rowCount++; if (!hasFixedSize(type)) { 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 f6ec4fd1c8..c043b79f11 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieDrawable.java @@ -311,6 +311,8 @@ public void setOnFinishCallback(Runnable callback, int frame) { } private boolean genCacheSend; + private boolean allowDrawFramesWhileCacheGenerating; + protected Runnable loadFrameRunnable = new Runnable() { private long lastUpdate = 0; @@ -365,6 +367,10 @@ public void run() { if (precache && bitmapsCache != null) { try { result = bitmapsCache.getFrame(currentFrame / framesPerUpdates, backgroundBitmap); + if (!bitmapsCache.needGenCache() && allowDrawFramesWhileCacheGenerating && nativePtr != 0) { + destroy(nativePtr); + nativePtr = 0; + } } catch (Exception e) { FileLog.e(e); } @@ -376,7 +382,14 @@ public void run() { genCacheSend = true; uiHandler.post(uiRunnableGenerateCache); } - result = -1; + if (allowDrawFramesWhileCacheGenerating) { + if (nativePtr == 0) { + nativePtr = create(args.file.toString(), args.json, width, height, new int[3], false, args.colorReplacement, false, args.fitzModifier); + } + result = getFrame(nativePtr, currentFrame, backgroundBitmap, width, height, backgroundBitmap.getRowBytes(), true); + } else { + result = -1; + } } if (result == -1) { uiHandler.post(uiRunnableNoFrame); @@ -502,7 +515,7 @@ public RLottieDrawable(File file, int w, int h, BitmapsCache.CacheOptions cacheO if (shouldLimitFps && metaData[1] < 60) { shouldLimitFps = false; } - bitmapsCache = new BitmapsCache(file, this, cacheOptions, w, h, !limitFps); + bitmapsCache = new BitmapsCache(file, this, cacheOptions, w, h, !limitFps); } else { nativePtr = create(file.getAbsolutePath(), null, w, h, metaData, precache, colorReplacement, shouldLimitFps, fitzModifier); if (nativePtr == 0) { @@ -988,7 +1001,7 @@ protected boolean scheduleNextGetFrame() { if (loadFrameTask != null || nextRenderingBitmap != null || !canLoadFrames() || loadingInBackground || destroyWhenDone || !isRunning && (!decodeSingleFrame || decodeSingleFrame && singleFrameDecoded)) { return false; } - if (generatingCache) { + if (generatingCache && !allowDrawFramesWhileCacheGenerating) { return false; } if (!newColorUpdates.isEmpty()) { @@ -1337,7 +1350,6 @@ public int getNextFrame(Bitmap bitmap) { return -1; } int framesPerUpdates = shouldLimitFps ? 2 : 1; - int result = getFrame(generateCacheNativePtr, generateCacheFramePointer, bitmap, width, height, bitmap.getRowBytes(), true); if (result == -5) { try { @@ -1397,10 +1409,6 @@ public boolean canLoadFrames() { } } - public void setAllowDrawFramesWhileCacheGenerating(boolean allowDrawWhileCacheGenerating) { - //TODO - } - private class NativePtrArgs { public int[] colorReplacement; public int fitzModifier; @@ -1440,6 +1448,10 @@ public void checkCache(Runnable onReady) { } } + public void setAllowDrawFramesWhileCacheGenerating(boolean allow) { + allowDrawFramesWhileCacheGenerating = allow; + } + private class LottieMetadata { float fr; float op; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/AnimatedEmojiEffect.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/AnimatedEmojiEffect.java index 8437bcbbab..aa9b83375b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/AnimatedEmojiEffect.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/AnimatedEmojiEffect.java @@ -11,13 +11,13 @@ import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.R; -import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; import org.telegram.tgnet.TLRPC; import org.telegram.ui.Components.AnimatedEmojiDrawable; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.RLottieDrawable; +import org.telegram.ui.EmojiAnimationsOverlay; import java.util.ArrayList; @@ -37,6 +37,7 @@ public class AnimatedEmojiEffect { ImageReceiver effectImageReceiver; int animationIndex = -1; + private static int currentIndex; private AnimatedEmojiEffect(AnimatedEmojiDrawable animatedEmojiDrawable, int currentAccount, boolean longAnimation, boolean showGeneric) { this.animatedEmojiDrawable = animatedEmojiDrawable; @@ -44,8 +45,11 @@ private AnimatedEmojiEffect(AnimatedEmojiDrawable animatedEmojiDrawable, int cur this.currentAccount = currentAccount; this.showGeneric = showGeneric; startTime = System.currentTimeMillis(); - if (!longAnimation && showGeneric && LiteMode.isEnabled(LiteMode.FLAG_ANIMATED_EMOJI_CHAT)) { + if (showGeneric && LiteMode.isEnabled(LiteMode.FLAG_ANIMATED_EMOJI_CHAT)) { effectImageReceiver = new ImageReceiver(); + if (longAnimation) { + effectImageReceiver.setAllowDrawWhileCacheGenerating(true); + } } } @@ -84,7 +88,17 @@ public void draw(Canvas canvas) { } } if (effectImageReceiver != null && showGeneric) { - effectImageReceiver.draw(canvas); + boolean isLastFrame = effectImageReceiver.getLottieAnimation() != null && effectImageReceiver.getLottieAnimation().isLastFrame(); + if (!isLastFrame) { + if (longAnimation) { + canvas.save(); + canvas.translate(bounds.width() / 3f, 0); + effectImageReceiver.draw(canvas); + canvas.restore(); + } else { + effectImageReceiver.draw(canvas); + } + } } canvas.save(); @@ -103,7 +117,7 @@ public void draw(Canvas canvas) { firsDraw = false; } - public boolean done() { + public boolean isDone() { return System.currentTimeMillis() - startTime > 2500; } @@ -118,7 +132,13 @@ public void setView(View view) { if (emojicon != null) { TLRPC.TL_availableReaction reaction = MediaDataController.getInstance(currentAccount).getReactionsMap().get(emojicon); if (reaction != null && reaction.around_animation != null) { - effectImageReceiver.setImage(ImageLocation.getForDocument(reaction.around_animation), ReactionsEffectOverlay.getFilterForAroundAnimation(), null, null, reaction.around_animation, 0); + if (longAnimation) { + effectImageReceiver.setUniqKeyPrefix(currentIndex++ + " "); + int w = EmojiAnimationsOverlay.getFilterWidth(); + effectImageReceiver.setImage(ImageLocation.getForDocument(reaction.around_animation), w + "_" + w + "_pcache_compress", null, null, reaction.around_animation, 0); + } else { + effectImageReceiver.setImage(ImageLocation.getForDocument(reaction.around_animation), ReactionsEffectOverlay.getFilterForAroundAnimation(), null, null, reaction.around_animation, 0); + } imageSet = true; } } @@ -136,7 +156,13 @@ public void setView(View view) { if (animationIndex < 0) { animationIndex = Math.abs(Utilities.fastRandom.nextInt() % set.documents.size()); } - effectImageReceiver.setImage(ImageLocation.getForDocument(set.documents.get(animationIndex)), "60_60", null, null, set.documents.get(animationIndex), 0); + if (longAnimation) { + effectImageReceiver.setUniqKeyPrefix(currentIndex++ + " "); + int w = EmojiAnimationsOverlay.getFilterWidth(); + effectImageReceiver.setImage(ImageLocation.getForDocument(set.documents.get(animationIndex)), w + "_" + w + "_pcache_compress", null, null, set.documents.get(animationIndex), 0); + } else { + effectImageReceiver.setImage(ImageLocation.getForDocument(set.documents.get(animationIndex)), "60_60", null, null, set.documents.get(animationIndex), 0); + } } } 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 6024ed7d52..bfda46e456 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 @@ -295,7 +295,13 @@ private MessageObject findPrimaryObject() { } private boolean isMessageTypeAllowed(MessageObject obj) { - return MessageObject.isPhoto(obj.messageOwner) && MessageObject.getMedia(obj.messageOwner).webpage == null || obj.getDocument() != null && (MessageObject.isVideoDocument(obj.getDocument()) || MessageObject.isGifDocument(obj.getDocument())); + return obj != null && !obj.needDrawBluredPreview() && ( + MessageObject.isPhoto(obj.messageOwner) && MessageObject.getMedia(obj.messageOwner).webpage == null || + obj.getDocument() != null && ( + MessageObject.isVideoDocument(obj.getDocument()) || + MessageObject.isGifDocument(obj.getDocument()) + ) + ); } public void setSelectedMessages(List messages) { 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 f17ccc7f5a..377fcbefe3 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 @@ -342,7 +342,7 @@ private void createTransition(boolean enter) { reactionsContainerLayout.getLocationOnScreen(location); } windowView.getLocationOnScreen(windowLocation); - float y = location[1] - windowLocation[1] - AndroidUtilities.dp(44) - AndroidUtilities.dp(52) - (selectAnimatedEmojiDialog.includeHint ? AndroidUtilities.dp(26) : 0); + float y = location[1] - windowLocation[1] - AndroidUtilities.dp(44) - AndroidUtilities.dp(52) - (selectAnimatedEmojiDialog.includeHint ? AndroidUtilities.dp(26) : 0) + reactionsContainerLayout.getTopOffset(); if (y + containerView.getMeasuredHeight() > windowView.getMeasuredHeight() - AndroidUtilities.dp(32)) { y = windowView.getMeasuredHeight() - AndroidUtilities.dp(32) - containerView.getMeasuredHeight(); } @@ -767,6 +767,14 @@ protected void dispatchDraw(Canvas canvas) { shadow.draw(canvas); canvas.drawRoundRect(drawingRect, radius, radius, backgroundPaint); } + if (reactionsContainerLayout.hintView != null) { + canvas.save(); + canvas.translate(drawingRect.left, drawingRect.top + reactionsContainerLayout.hintView.getY()); + canvas.saveLayerAlpha( 0, 0, reactionsContainerLayout.hintView.getMeasuredWidth(), reactionsContainerLayout.hintView.getMeasuredHeight(), (int) (255 * reactionsContainerLayout.hintView.getAlpha() * (1f - enterTransitionProgress)), Canvas.ALL_SAVE_FLAG); + reactionsContainerLayout.hintView.draw(canvas); + canvas.restore(); + canvas.restore(); + } float rightDelta = drawingRect.left - reactionsContainerLayout.rect.left + (drawingRect.width() - reactionsContainerLayout.rect.width()); @@ -794,7 +802,7 @@ protected void dispatchDraw(Canvas canvas) { int restoreCount = canvas.save(); - canvas.translate(drawingRect.left, drawingRect.top + reactionsContainerLayout.expandSize() * (1f - enterTransitionProgress)); + canvas.translate(drawingRect.left, drawingRect.top + (reactionsContainerLayout.getTopOffset() + reactionsContainerLayout.expandSize()) * (1f - enterTransitionProgress)); float a = selectAnimatedEmojiDialog.emojiSearchGridView.getVisibility() == View.VISIBLE ? selectAnimatedEmojiDialog.emojiSearchGridView.getAlpha() : 0; float alpha = Math.max(1f - a, 1f - enterTransitionProgress); @@ -974,6 +982,13 @@ protected void dispatchDraw(Canvas canvas) { } } + private float getBlurOffset() { + if (type == TYPE_STORY) { + return containerView.getY() - AndroidUtilities.statusBarHeight; + } + return containerView.getY() + windowView.getY(); + } + public void setRecentReactions(List reactions) { selectAnimatedEmojiDialog.setRecentReactions(reactions); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionImageHolder.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionImageHolder.java new file mode 100644 index 0000000000..a40e8b720f --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionImageHolder.java @@ -0,0 +1,113 @@ +package org.telegram.ui.Components.Reactions; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; +import android.graphics.RectF; +import android.view.View; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.DocumentObject; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.MediaDataController; +import org.telegram.messenger.SvgHelper; +import org.telegram.messenger.UserConfig; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedEmojiDrawable; + +import java.util.Objects; + +public class ReactionImageHolder { + + final ImageReceiver imageReceiver; + public AnimatedEmojiDrawable animatedEmojiDrawable; + + private final Rect bounds = new Rect(); + ReactionsLayoutInBubble.VisibleReaction reaction; + private final int currentAccount = UserConfig.selectedAccount; + ReactionsLayoutInBubble.VisibleReaction currentReaction; + private final View parent; + private boolean attached; + float alpha = 1f; + + public ReactionImageHolder(View parent) { + this.parent = parent; + imageReceiver = new ImageReceiver(parent); + imageReceiver.setAllowLoadingOnAttachedOnly(true); + } + + public void setVisibleReaction(ReactionsLayoutInBubble.VisibleReaction currentReaction) { + if (Objects.equals(this.currentReaction, currentReaction)) { + return; + } + imageReceiver.clearImage(); + if (animatedEmojiDrawable != null) { + animatedEmojiDrawable.removeView(parent); + animatedEmojiDrawable = null; + } + + this.currentReaction = currentReaction; + if (currentReaction.emojicon != null) { + TLRPC.TL_availableReaction defaultReaction = MediaDataController.getInstance(currentAccount).getReactionsMap().get(currentReaction.emojicon); + if (defaultReaction != null) { + SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(defaultReaction.select_animation, Theme.key_windowBackgroundWhiteGrayIcon, 0.2f); + imageReceiver.setImage(ImageLocation.getForDocument(defaultReaction.select_animation), "60_60", null, null, svgThumb, 0, "tgs", currentReaction, 0); +// imageReceiver.setAllowStartAnimation(false); +// imageReceiver.setAutoRepeatCount(1); + } + } else { + animatedEmojiDrawable = new AnimatedEmojiDrawable(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES_LARGE, UserConfig.selectedAccount, currentReaction.documentId); + if (attached) { + animatedEmojiDrawable.addView(parent); + } + animatedEmojiDrawable.setColorFilter(new PorterDuffColorFilter(Color.BLACK, PorterDuff.Mode.SRC_ATOP)); + } + } + + public void draw(Canvas canvas) { + if (animatedEmojiDrawable != null) { + if (animatedEmojiDrawable.getImageReceiver() != null) { + animatedEmojiDrawable.getImageReceiver().setRoundRadius((int) (bounds.width() * 0.1f)); + } + animatedEmojiDrawable.setBounds(bounds); + animatedEmojiDrawable.setAlpha((int) (255 * alpha)); + animatedEmojiDrawable.draw(canvas); + } else { + imageReceiver.setImageCoords(bounds.left, bounds.top, bounds.width(), bounds.height()); + imageReceiver.setAlpha(alpha); + imageReceiver.draw(canvas); + } + } + + public void setBounds(Rect bounds) { + this.bounds.set(bounds); + } + + public void onAttachedToWindow(boolean attached) { + this.attached = attached; + if (attached) { + imageReceiver.onAttachedToWindow(); + if (animatedEmojiDrawable != null) { + animatedEmojiDrawable.addView(parent); + } + } else { + imageReceiver.onDetachedFromWindow(); + if (animatedEmojiDrawable != null) { + animatedEmojiDrawable.removeView(parent); + } + } + } + + public void setAlpha(float alpha) { + this.alpha = alpha; + } + + public void play() { + imageReceiver.startAnimation(); + } +} 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 8d7c4928e5..6d5713f4ea 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 @@ -638,7 +638,8 @@ protected void onDetachedFromWindow() { color = Color.WHITE; } animatedEmojiDrawable.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)); - effectImageView.setAnimatedEmojiEffect(AnimatedEmojiEffect.createFrom(animatedEmojiDrawable, animationType == LONG_ANIMATION, true)); + boolean longAnimation = animationType == LONG_ANIMATION; + effectImageView.setAnimatedEmojiEffect(AnimatedEmojiEffect.createFrom(animatedEmojiDrawable, longAnimation, !longAnimation)); windowView.setClipChildren(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 b3798b2336..ef0f86d56c 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 @@ -999,5 +999,9 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(emojicon, documentId); } + + public boolean isSame(TLRPC.Reaction reaction) { + return false; + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionsUtils.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionsUtils.java index 832579cc28..7c69975f7c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionsUtils.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionsUtils.java @@ -22,6 +22,16 @@ public static boolean compare(TLRPC.Reaction reaction, ReactionsLayoutInBubble.V return false; } + public static boolean compare(TLRPC.Reaction reaction, TLRPC.Reaction reaction2) { + if (reaction instanceof TLRPC.TL_reactionEmoji && reaction2 instanceof TLRPC.TL_reactionEmoji && TextUtils.equals(((TLRPC.TL_reactionEmoji) reaction).emoticon, ((TLRPC.TL_reactionEmoji) reaction2).emoticon)) { + return true; + } + if (reaction instanceof TLRPC.TL_reactionCustomEmoji && reaction2 instanceof TLRPC.TL_reactionCustomEmoji && ((TLRPC.TL_reactionCustomEmoji) reaction).document_id == ((TLRPC.TL_reactionCustomEmoji) reaction2).document_id) { + return true; + } + return false; + } + public static TLRPC.Reaction toTLReaction(ReactionsLayoutInBubble.VisibleReaction visibleReaction) { if (visibleReaction.emojicon != null) { TLRPC.TL_reactionEmoji emoji = new TLRPC.TL_reactionEmoji(); 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 831589fc5b..fc891509d0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactionsContainerLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactionsContainerLayout.java @@ -19,8 +19,8 @@ import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.provider.Settings; -import android.util.Log; import android.util.Property; +import android.util.TypedValue; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; @@ -62,7 +62,6 @@ 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; @@ -99,7 +98,6 @@ public void set(ReactionsContainerLayout object, Float value) { private final static int ALPHA_DURATION = 150; private final static float SIDE_SCALE = 0.6f; - private final static float SCALE_PROGRESS = 0.75f; private final static float CLIP_PROGRESS = 0.25f; public final RecyclerListView recyclerListView; public final float durationScale; @@ -118,6 +116,7 @@ public void set(ReactionsContainerLayout object, Float value) { private MessageObject messageObject; private int currentAccount; private long waitingLoadingChatId; + private boolean isTop; private boolean mirrorX; private boolean isFlippedVertically; @@ -134,6 +133,7 @@ public void set(ReactionsContainerLayout object, Float value) { private LinearLayoutManager linearLayoutManager; private RecyclerView.Adapter listAdapter; + RectF rectF = new RectF(); HashSet selectedReactions = new HashSet<>(); @@ -178,6 +178,9 @@ public void set(ReactionsContainerLayout object, Float value) { public boolean skipEnterAnimation; public boolean isHiddenNextReaction = true; private Runnable onSwitchedToLoopView; + private boolean hasHint; + public TextView hintView; + private float bubblesOffset; public ReactionsContainerLayout(int type, BaseFragment fragment, @NonNull Context context, int currentAccount, Theme.ResourcesProvider resourcesProvider) { super(context); @@ -360,7 +363,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int break; } - int size = getLayoutParams().height - getPaddingTop() - getPaddingBottom(); + int size = getLayoutParams().height - (hasHint ? AndroidUtilities.dp(20) : 0) - getPaddingTop() - getPaddingBottom(); view.setLayoutParams(new RecyclerView.LayoutParams(size - AndroidUtilities.dp(12), size)); return new RecyclerListView.Holder(view); } @@ -630,7 +633,7 @@ private void setVisibleReactionsList(List visibleReactions) { HashSet set = new HashSet<>(); for (int i = 0; i < visibleReactions.size(); i++) { @@ -1324,6 +1345,32 @@ public void reset() { invalidate(); } + public void setHint(String storyReactionsHint) { + hasHint = true; + hintView = new TextView(getContext()); + hintView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); + hintView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + hintView.setText(storyReactionsHint); + hintView.setAlpha(0.5f); + hintView.setGravity(Gravity.CENTER_HORIZONTAL); + addView(hintView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 6, 0, 0)); + + ((LayoutParams) nextRecentReaction.getLayoutParams()).topMargin = AndroidUtilities.dp(20); + ((LayoutParams) recyclerListView.getLayoutParams()).topMargin = AndroidUtilities.dp(20); + } + + public void setTop(boolean isTop) { + this.isTop = isTop; + } + + public float getTopOffset() { + return hasHint ? AndroidUtilities.dp(20) : 0; + } + + public void setBubbleOffset(float v) { + bubblesOffset = v; + } + private final class LeftRightShadowsListener extends RecyclerView.OnScrollListener { private boolean leftVisible, rightVisible; private ValueAnimator leftAnimator, rightAnimator; 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 80d56f6fb2..61955dd9a9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java @@ -446,6 +446,9 @@ protected void dispatchDraw(Canvas canvas) { } public float getPhotoVideoOptionsAlpha(float progress) { + if (isArchivedOnlyStoriesView()) { + return 0; + } 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; @@ -1440,7 +1443,9 @@ public void onLayout(int l, int t, int r, int b) { calendarDrawable.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_actionBarActionModeDefaultIcon), PorterDuff.Mode.MULTIPLY)); photoVideoOptionsItem.setImageDrawable(calendarDrawable); photoVideoOptionsItem.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - actionBar.addView(photoVideoOptionsItem, LayoutHelper.createFrame(48, 56, Gravity.RIGHT | Gravity.BOTTOM)); + if (!isArchivedOnlyStoriesView()) { + actionBar.addView(photoVideoOptionsItem, LayoutHelper.createFrame(48, 56, Gravity.RIGHT | Gravity.BOTTOM)); + } photoVideoOptionsItem.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { @@ -1757,7 +1762,7 @@ public void setTranslationX(float translationX) { float photoVideoOptionsAlpha = getPhotoVideoOptionsAlpha(scrollProgress); photoVideoOptionsItem.setAlpha(photoVideoOptionsAlpha); - photoVideoOptionsItem.setVisibility((photoVideoOptionsAlpha == 0 || !canShowSearchItem()) ? INVISIBLE : View.VISIBLE); + photoVideoOptionsItem.setVisibility((photoVideoOptionsAlpha == 0 || !canShowSearchItem() || isArchivedOnlyStoriesView()) ? INVISIBLE : View.VISIBLE); } else { searchItem.setAlpha(0.0f); } @@ -1908,7 +1913,7 @@ protected void dispatchDraw(Canvas canvas) { } 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); + archivedHintLayout = new StaticLayout(LocaleController.getString(isArchivedOnlyStoriesView() ? R.string.ProfileStoriesArchiveChannelHint : R.string.ProfileStoriesArchiveHint), archivedHintPaint, width, Layout.Alignment.ALIGN_CENTER, 1f, 0f, false); archivedHintLayoutWidth = 0; archivedHintLayoutLeft = width; for (int i = 0; i < archivedHintLayout.getLineCount(); ++i) { @@ -2573,6 +2578,10 @@ protected boolean isStoriesView() { return false; } + protected boolean isArchivedOnlyStoriesView() { + return false; + } + protected boolean includeStories() { return true; } @@ -3149,7 +3158,7 @@ public void onPageScrolled(float progress) { float photoVideoOptionsAlpha = getPhotoVideoOptionsAlpha(progress); photoVideoOptionsItem.setAlpha(photoVideoOptionsAlpha); - photoVideoOptionsItem.setVisibility((photoVideoOptionsAlpha == 0 || !canShowSearchItem()) ? INVISIBLE : View.VISIBLE); + photoVideoOptionsItem.setVisibility((photoVideoOptionsAlpha == 0 || !canShowSearchItem() || isArchivedOnlyStoriesView()) ? INVISIBLE : View.VISIBLE); if (canShowSearchItem()) { if (searchItemState == 1) { searchItem.setAlpha(progress); @@ -3970,7 +3979,7 @@ public boolean onTouchEvent(MotionEvent ev) { float photoVideoOptionsAlpha = getPhotoVideoOptionsAlpha(scrollProgress); photoVideoOptionsItem.setAlpha(photoVideoOptionsAlpha); - photoVideoOptionsItem.setVisibility((photoVideoOptionsAlpha == 0 || !canShowSearchItem()) ? INVISIBLE : View.VISIBLE); + photoVideoOptionsItem.setVisibility((photoVideoOptionsAlpha == 0 || !canShowSearchItem() || isArchivedOnlyStoriesView()) ? INVISIBLE : View.VISIBLE); } else { searchItem.setAlpha(0.0f); } @@ -6865,7 +6874,11 @@ private class StoriesAdapter extends SharedPhotoVideoAdapter { 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); + if (isArchive && !isStoriesView() || !isArchive && isArchivedOnlyStoriesView()) { + storiesList = null; + } else { + storiesList = profileActivity.getMessagesController().getStoriesController().getStoriesList(dialog_id, isArchive ? StoriesController.StoriesList.TYPE_ARCHIVE : StoriesController.StoriesList.TYPE_PINNED); + } if (storiesList != null) { id = storiesList.link(); } 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 03fd862ec8..61dbbf28dd 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayout.java @@ -16,6 +16,8 @@ import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Rect; @@ -56,7 +58,7 @@ public class SizeNotifierFrameLayout extends FrameLayout { protected int keyboardHeight; private int bottomClip; - private SizeNotifierFrameLayoutDelegate delegate; + protected SizeNotifierFrameLayoutDelegate delegate; private boolean occupyStatusBar = true; private WallpaperParallaxEffect parallaxEffect; private float translationX; @@ -110,7 +112,13 @@ public class SizeNotifierFrameLayout extends FrameLayout { // public void invalidateBlur() { + if (!SharedConfig.chatBlurEnabled()) { + return; + } invalidateBlur = true; + if (!blurIsRunning || blurGeneratingTuskIsRunning) { + return; + } invalidate(); } 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 618ddab43a..f250b524a2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayoutPhoto.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayoutPhoto.java @@ -17,20 +17,15 @@ import org.telegram.messenger.AndroidUtilities; -public class SizeNotifierFrameLayoutPhoto extends FrameLayout { +public class SizeNotifierFrameLayoutPhoto extends SizeNotifierFrameLayout { private Activity activity; private Rect rect = new Rect(); private int keyboardHeight; - private SizeNotifierFrameLayoutPhotoDelegate delegate; private WindowManager windowManager; private boolean withoutWindow; private boolean useSmoothKeyboard; - public interface SizeNotifierFrameLayoutPhotoDelegate { - void onSizeChanged(int keyboardHeight, boolean isWidthGreater); - } - public SizeNotifierFrameLayoutPhoto(Context context, Activity activity, boolean smoothKeyboard) { super(context); setActivity(activity); @@ -41,10 +36,6 @@ public void setActivity(Activity activity) { this.activity = activity; } - public void setDelegate(SizeNotifierFrameLayoutPhotoDelegate sizeNotifierFrameLayoutPhotoDelegate) { - delegate = sizeNotifierFrameLayoutPhotoDelegate; - } - public void setWithoutWindow(boolean value) { withoutWindow = value; } @@ -55,10 +46,12 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { notifyHeightChanged(); } + @Override public int getKeyboardHeight() { return keyboardHeight; } + @Override public int measureKeyboardHeight() { View rootView = getRootView(); getWindowVisibleDisplayFrame(rect); @@ -74,8 +67,9 @@ public int measureKeyboardHeight() { } } + @Override public void notifyHeightChanged() { - if (delegate != null) { + if (super.delegate != null) { keyboardHeight = measureKeyboardHeight(); final boolean isWidthGreater = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y; post(() -> { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SpoilersShowcase.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SpoilersShowcase.java new file mode 100644 index 0000000000..d8d71c7b52 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SpoilersShowcase.java @@ -0,0 +1,62 @@ +package org.telegram.ui.Components; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.Components.spoilers.SpoilerEffect; +import org.telegram.ui.Components.spoilers.SpoilerEffect2; + +public class SpoilersShowcase extends BaseFragment { + + @Override + public View createView(Context context) { + final ViewGroup renderView = new FrameLayout(context) { + private SpoilerEffect2 spoilerEffect; + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (spoilerEffect == null) { + spoilerEffect = SpoilerEffect2.getInstance(this); + } + } + + @Override + protected void dispatchDraw(Canvas canvas) { + spoilerEffect.draw(canvas, this, getMeasuredWidth(), getMeasuredHeight()); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (spoilerEffect != null) { + spoilerEffect.detach(this); + spoilerEffect = null; + } + } + }; + + FrameLayout container = new FrameLayout(context); + container.addView(renderView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + return fragmentView = container; + } + + @Override + public boolean isSwipeBackEnabled(MotionEvent event) { + return true; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StatusBadgeComponent.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StatusBadgeComponent.java new file mode 100644 index 0000000000..e207e00a5d --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StatusBadgeComponent.java @@ -0,0 +1,66 @@ +package org.telegram.ui.Components; + +import android.graphics.drawable.Drawable; +import android.view.View; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.Premium.PremiumGradient; + +public class StatusBadgeComponent { + + private final AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable statusDrawable; + private Drawable verifiedDrawable; + + public StatusBadgeComponent(View parentView) { + statusDrawable = new AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable(parentView, AndroidUtilities.dp(18)); + } + + public Drawable updateDrawable(TLObject object, int colorFilter, boolean animated) { + if (object instanceof TLRPC.User) { + return updateDrawable((TLRPC.User) object, null, colorFilter, animated); + } else if (object instanceof TLRPC.Chat) { + return updateDrawable(null, (TLRPC.Chat) object, colorFilter, animated); + } + return updateDrawable(null, null, colorFilter, animated); + } + + public Drawable updateDrawable(TLRPC.User user, TLRPC.Chat chat, int colorFilter, boolean animated) { + if (chat != null && chat.verified) { + statusDrawable.set(verifiedDrawable = (verifiedDrawable == null ? new CombinedDrawable(Theme.dialogs_verifiedDrawable, Theme.dialogs_verifiedCheckDrawable) : verifiedDrawable), animated); + statusDrawable.setColor(null); + return statusDrawable; + } + if (user != null && user.verified) { + statusDrawable.set(verifiedDrawable = (verifiedDrawable == null ? new CombinedDrawable(Theme.dialogs_verifiedDrawable, Theme.dialogs_verifiedCheckDrawable) : verifiedDrawable), animated); + statusDrawable.setColor(null); + } else if (user != null && user.emoji_status instanceof TLRPC.TL_emojiStatus) { + statusDrawable.set(((TLRPC.TL_emojiStatus) user.emoji_status).document_id, animated); + statusDrawable.setColor(null); + } else if (user != null && user.emoji_status instanceof TLRPC.TL_emojiStatusUntil && ((TLRPC.TL_emojiStatusUntil) user.emoji_status).until > (int) (System.currentTimeMillis() / 1000)) { + statusDrawable.set(((TLRPC.TL_emojiStatusUntil) user.emoji_status).document_id, animated); + statusDrawable.setColor(null); + } else if (user != null && user.premium) { + statusDrawable.set(PremiumGradient.getInstance().premiumStarDrawableMini, animated); + statusDrawable.setColor(colorFilter); + } else { + statusDrawable.set((Drawable) null, animated); + statusDrawable.setColor(null); + } + return statusDrawable; + } + + public Drawable getDrawable() { + return statusDrawable; + } + + public void onAttachedToWindow() { + statusDrawable.attach(); + } + + public void onDetachedFromWindow() { + statusDrawable.detach(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoCompressButton.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoCompressButton.java new file mode 100644 index 0000000000..ff3ee18136 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoCompressButton.java @@ -0,0 +1,155 @@ +package org.telegram.ui.Components; + +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.PorterDuffXfermode; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.View; + +import androidx.annotation.NonNull; + +import org.telegram.messenger.AndroidUtilities; + +public class VideoCompressButton extends View { + + public static final int STATE_GIF = 0; + public static final int STATE_SD = 1; + public static final int STATE_HD = 2; + + private final AnimatedTextView.AnimatedTextDrawable textDrawable; + private final AnimatedTextView.AnimatedTextDrawable sizeTextDrawable; + private final Paint strokePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint fillPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint clearPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private boolean disabled; + private final AnimatedFloat disabledT = new AnimatedFloat(this, 0, 300, CubicBezierInterpolator.EASE_OUT_QUINT); + + public VideoCompressButton(Context context) { + super(context); + + textDrawable = new AnimatedTextView.AnimatedTextDrawable(true, false, false); + textDrawable.setAnimationProperties(.4f, 0, 360, CubicBezierInterpolator.EASE_OUT_QUINT); + textDrawable.setTypeface(AndroidUtilities.getTypeface("fonts/num.otf")); + textDrawable.setTextColor(0xffffffff); + textDrawable.setTextSize(dpf2(10.6f)); + textDrawable.setCallback(this); + textDrawable.setGravity(Gravity.CENTER); + + sizeTextDrawable = new AnimatedTextView.AnimatedTextDrawable(true, false, false); + sizeTextDrawable.setAnimationProperties(.2f, 0, 360, CubicBezierInterpolator.EASE_OUT_QUINT); + sizeTextDrawable.setTypeface(AndroidUtilities.getTypeface("fonts/num.otf")); + sizeTextDrawable.setTextColor(0xffffffff); + sizeTextDrawable.setTextSize(dpf2(8.6f)); + sizeTextDrawable.setCallback(this); + sizeTextDrawable.setGravity(Gravity.RIGHT); + sizeTextDrawable.getPaint().setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + sizeTextDrawable.setOverrideFullWidth(AndroidUtilities.displaySize.x); + + strokePaint.setColor(0xffffffff); + strokePaint.setStyle(Paint.Style.STROKE); + + fillPaint.setColor(0xffffffff); + + clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + } + + private final int[] sizes = new int[] { + 144, // 144x256 + 240, + 360, // 360x640 + 480, + 720, // 720x1280 + 1080, // 1080x1920 + 1440, // 1440x2560, 2K + 2160, // 2160x3840, 4K + }; + + public void setState(boolean enabled, boolean muted, int mn) { + this.disabled = !enabled || muted; + if (muted) { + textDrawable.setText("GIF"); + sizeTextDrawable.setText("", true); + } else { + textDrawable.setText(mn >= 720 ? "HD" : "SD"); + int index = -1; + for (int i = sizes.length - 1; i >= 0; i--) { + if (mn >= sizes[i]) { + index = i; + break; + } + } + if (index < 0) + sizeTextDrawable.setText("", true); + else if (index == 6) + sizeTextDrawable.setText("2K", TextUtils.isEmpty(sizeTextDrawable.getText())); + else if (index == 7) + sizeTextDrawable.setText("4K", TextUtils.isEmpty(sizeTextDrawable.getText())); + else + sizeTextDrawable.setText("" + sizes[index], TextUtils.isEmpty(sizeTextDrawable.getText())); + } + setClickable(!this.disabled); + invalidate(); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + + canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), 0xFF, Canvas.ALL_SAVE_FLAG); + final float disabled = disabledT.set(this.disabled); + strokePaint.setAlpha((int) (0xFF * (1f - .35f * disabled))); + strokePaint.setStrokeWidth(dpf2(1.33f)); + + float w = Math.max(dpf2(21.33f), dpf2(6) + textDrawable.getCurrentWidth()), h = dpf2(17.33f); + AndroidUtilities.rectTmp.set( + (getWidth() - w) / 2f, + (getHeight() - h) / 2f, + (getWidth() + w) / 2f, + (getHeight() + h) / 2f + ); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dpf2(4), dpf2(4), strokePaint); + + AndroidUtilities.rectTmp2.set(0, (int) ((getHeight() - h) / 2f), getWidth(), (int) ((getHeight() + h) / 2f)); + textDrawable.setBounds(AndroidUtilities.rectTmp2); + textDrawable.setAlpha((int) (0xFF * (1f - .35f * disabled))); + textDrawable.draw(canvas); + + float sw = sizeTextDrawable.isNotEmpty() * dpf2(2) + sizeTextDrawable.getCurrentWidth(); + float sh = dpf2(8.33f); + + AndroidUtilities.rectTmp2.set( + (int) (getWidth() / 2f + dpf2(16) - sw), + (int) (getHeight() / 2f - dpf2(14)), + (int) (getWidth() / 2f + dpf2(16)), + (int) (getHeight() / 2f - dpf2(14) + sh) + ); + AndroidUtilities.rectTmp.set(AndroidUtilities.rectTmp2); + AndroidUtilities.rectTmp.inset(-dpf2(1.33f), -dpf2(1.33f)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dpf2(1.66f), dpf2(1.66f), clearPaint); + + canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), 0xFF, Canvas.ALL_SAVE_FLAG); + AndroidUtilities.rectTmp.set(AndroidUtilities.rectTmp2); + fillPaint.setAlpha((int) (0xFF * (1f - .35f * disabled) * sizeTextDrawable.isNotEmpty())); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dpf2(1.66f), dpf2(1.66f), fillPaint); + AndroidUtilities.rectTmp2.offset((int) (-dpf2(1.33f)), 0); + canvas.save(); + sizeTextDrawable.setBounds(AndroidUtilities.rectTmp2); + sizeTextDrawable.draw(canvas); + canvas.restore(); + canvas.restore(); + + canvas.restore(); + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return textDrawable == who || sizeTextDrawable == who || super.verifyDrawable(who); + } +} 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 914015cea6..0e868013d2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoEditTextureView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoEditTextureView.java @@ -1,8 +1,9 @@ package org.telegram.ui.Components; import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Matrix; import android.graphics.SurfaceTexture; -import android.util.Log; import android.view.Surface; import android.view.TextureView; @@ -19,6 +20,7 @@ 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; @@ -77,11 +79,12 @@ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int hei } Surface s = new Surface(surfaceTexture); currentVideoPlayer.setSurface(s); - }, hdrInfo); + }, hdrInfo, uiBlurManager, width, height); + eglThread.updateUiBlurGradient(gradientTop, gradientBottom); + eglThread.updateUiBlurManager(uiBlurManager); if (videoWidth != 0 && videoHeight != 0) { eglThread.setVideoSize(videoWidth, videoHeight); } - eglThread.setSurfaceTextureSize(width, height); eglThread.requestRender(true, true, false); if (delegate != null) { delegate.onEGLThreadAvailable(eglThread); @@ -133,4 +136,37 @@ public void setViewRect(float x, float y, float w, float h) { public boolean containsPoint(float x, float y) { return x >= viewRect.x && x <= viewRect.x + viewRect.width && y >= viewRect.y && y <= viewRect.y + viewRect.height; } + + public Bitmap getUiBlurBitmap() { + if (eglThread == null) { + return null; + } + return eglThread.getUiBlurBitmap(); + } + + @Override + public void setTransform(@Nullable Matrix transform) { + super.setTransform(transform); + if (eglThread != null) { + eglThread.updateUiBlurTransform(transform, getWidth(), getHeight()); + } + } + + private int gradientTop, gradientBottom; + public void updateUiBlurGradient(int top, int bottom) { + if (eglThread == null) { + gradientTop = top; + gradientBottom = bottom; + return; + } + eglThread.updateUiBlurGradient(top, bottom); + } + + private BlurringShader.BlurManager uiBlurManager; + public void updateUiBlurManager(BlurringShader.BlurManager uiBlurManager) { + this.uiBlurManager = uiBlurManager; + if (eglThread != null) { + eglThread.updateUiBlurManager(uiBlurManager); + } + } } 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 c1b3e0392f..9fb2500fd8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoPlayer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoPlayer.java @@ -31,9 +31,11 @@ import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.MediaItem; +import com.google.android.exoplayer2.MediaMetadata; import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; +import com.google.android.exoplayer2.SeekParameters; import com.google.android.exoplayer2.analytics.AnalyticsListener; import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.audio.AudioCapabilities; @@ -112,6 +114,7 @@ public interface AudioVisualizerDelegate { private boolean isStreaming; private boolean autoplay; private boolean mixedAudio; + public boolean allowMultipleInstances; private boolean triedReinit; @@ -166,7 +169,7 @@ public VideoPlayer(boolean pauseOther, boolean audioDisabled) { public void didReceivedNotification(int id, int account, Object... args) { if (id == NotificationCenter.playerDidStartPlaying) { VideoPlayer p = (VideoPlayer) args[0]; - if (p != this && isPlaying()) { + if (p != this && isPlaying() && !allowMultipleInstances) { pause(); } } @@ -216,6 +219,8 @@ private void ensurePlayerCreated() { player.setVideoTextureView(textureView); } else if (surface != null) { player.setVideoSurface(surface); + } else if (surfaceView != null) { + player.setVideoSurfaceView(surfaceView); } player.setPlayWhenReady(autoplay); player.setRepeatMode(looping ? ExoPlayer.REPEAT_MODE_ALL : ExoPlayer.REPEAT_MODE_OFF); @@ -515,7 +520,12 @@ public void setVolume(float volume) { } public void seekTo(long positionMs) { + seekTo(positionMs, false); + } + + public void seekTo(long positionMs, boolean fast) { if (player != null) { + player.setSeekParameters(fast ? SeekParameters.CLOSEST_SYNC : SeekParameters.EXACT); player.seekTo(positionMs); } } @@ -548,11 +558,20 @@ public boolean isBuffering() { return player != null && lastReportedPlaybackState == ExoPlayer.STATE_BUFFERING; } + + private boolean handleAudioFocus = false; + public void handleAudioFocus(boolean handleAudioFocus) { + this.handleAudioFocus = handleAudioFocus; + if (player != null) { + player.setAudioAttributes(player.getAudioAttributes(), handleAudioFocus); + } + } + public void setStreamType(int type) { if (player != null) { player.setAudioAttributes(new AudioAttributes.Builder() .setUsage(type == AudioManager.STREAM_VOICE_CALL ? C.USAGE_VOICE_COMMUNICATION : C.USAGE_MEDIA) - .build(), false); + .build(), handleAudioFocus); } if (audioPlayer != null) { audioPlayer.setAudioAttributes(new AudioAttributes.Builder() 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 2e0a39a61a..6b49b0563f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoTimelinePlayView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoTimelinePlayView.java @@ -18,6 +18,7 @@ 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.drawable.Drawable; @@ -31,6 +32,7 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.FileLog; import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; import org.telegram.ui.ActionBar.Theme; import java.util.ArrayList; @@ -38,11 +40,9 @@ public class VideoTimelinePlayView extends View { private long videoLength; + private int videoWidth, videoHeight; private float progressLeft; private float progressRight = 1; - private Paint paint; - private Paint paint2; - private Paint paint3; private boolean pressedLeft; private boolean pressedRight; private boolean pressedPlay; @@ -85,18 +85,19 @@ public interface VideoTimelineViewDelegate { public static int TYPE_RIGHT = 1; public static int TYPE_PROGRESS = 2; + private final Paint whitePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint shadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint dimPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint cutPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint handlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + public VideoTimelinePlayView(Context context) { super(context); - paint = new Paint(Paint.ANTI_ALIAS_FLAG); - paint.setColor(0xffffffff); - paint2 = new Paint(); - 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)); + whitePaint.setColor(0xffffffff); + shadowPaint.setColor(0x26000000); + dimPaint.setColor(0x4d000000); + cutPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + handlePaint.setColor(0xff000000); exclusionRects.add(exclustionRect); } @@ -160,10 +161,10 @@ public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); - 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); + int width = getMeasuredWidth() - dp(10 * 2 + 12 * 2); + int startX = (int) (width * progressLeft) + dp(12 + 10); + int playX = (int) (width * playProgress) + dp(12 + 10); + int endX = (int) (width * progressRight) + dp(12 + 10); if (event.getAction() == MotionEvent.ACTION_DOWN) { getParent().requestDisallowInterceptTouchEvent(true); @@ -305,10 +306,35 @@ public void setVideoPath(String path, float left, float right) { mediaMetadataRetriever = new MediaMetadataRetriever(); progressLeft = left; progressRight = right; + if (playProgress < progressLeft) { + playProgress = progressLeft; + } else if (playProgress > progressRight) { + playProgress = progressRight; + } try { mediaMetadataRetriever.setDataSource(path); - String duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); - videoLength = Long.parseLong(duration); + String value; + value = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); + if (value != null) { + videoLength = Long.parseLong(value); + } + value = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH); + if (value != null) { + videoWidth = Integer.parseInt(value); + } + value = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT); + if (value != null) { + videoHeight = Integer.parseInt(value); + } + value = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION); + if (value != null) { + int orientation = Integer.parseInt(value); + if (orientation == 90 || orientation == 270) { + int temp = videoWidth; + videoWidth = videoHeight; + videoHeight = temp; + } + } } catch (Exception e) { FileLog.e(e); } @@ -348,14 +374,21 @@ private void reloadFrames(int frameNum) { return; } if (frameNum == 0) { - frameHeight = dp(40); - framesToLoad = Math.max(1, (getMeasuredWidth() - dp(16)) / frameHeight); - frameWidth = (int) Math.ceil((float) (getMeasuredWidth() - dp(16)) / (float) framesToLoad); + frameHeight = dp(38); + float aspectRatio = 1; + if (videoWidth != 0 && videoHeight != 0) { + aspectRatio = videoWidth / (float) videoHeight; + } + aspectRatio = Utilities.clamp(aspectRatio, 4 / 3f, 9f / 16f); + framesToLoad = Math.max(1, (int) Math.ceil((getMeasuredWidth() - dp(32)) / (frameHeight * aspectRatio))); + frameWidth = (int) Math.ceil((float) (getMeasuredWidth() - dp(32)) / (float) framesToLoad); frameTimeOffset = videoLength / framesToLoad; } currentTask = new AsyncTask() { private int frameNum = 0; + private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + @Override protected Bitmap doInBackground(Integer... objects) { frameNum = objects[0]; @@ -378,7 +411,7 @@ protected Bitmap doInBackground(Integer... objects) { 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, (frameWidth + w) / 2, (frameHeight + h) / 2); - canvas.drawBitmap(bitmap, srcRect, destRect, null); + canvas.drawBitmap(bitmap, srcRect, destRect, paint); bitmap.recycle(); bitmap = result; } @@ -430,7 +463,12 @@ public boolean isDragging() { return pressedPlay; } + private final AnimatedFloat loopProgress = new AnimatedFloat(0, this, 0, 200, CubicBezierInterpolator.EASE_BOTH); public void setProgress(float value) { + float d = videoLength == 0 ? 0 : 240 / (float) videoLength; + if (value < this.playProgress && value <= progressLeft + d && this.playProgress + d >= progressRight) { + loopProgress.set(1, true); + } playProgress = value; invalidate(); } @@ -461,34 +499,62 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } private Path clipPath = new Path(); + private boolean hasBlur; @Override protected void onDraw(Canvas canvas) { - int width = getMeasuredWidth() - dp(32); - int startX = (int) (width * progressLeft) + dp(16); - int endX = (int) (width * progressRight) + dp(16); + final float px = dpf2(12); + final float width = getMeasuredWidth() - px * 2; + final float startX = px + dp(10) + (int) ((width - dp(10 * 2)) * progressLeft); + final float endX = px + dp(10) + (int) ((width - dp(10 * 2)) * progressRight); - int top = dp(2 + 4); - int end = dp(48); + final float top = dp(2 + 4); + final float end = top + dp(38); - canvas.save(); - 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); + AndroidUtilities.rectTmp.set(px, top, px + width, end); + if (customBlur()) { + canvas.save(); + clipPath.rewind(); + clipPath.addRoundRect(AndroidUtilities.rectTmp, dp(6), dp(6), Path.Direction.CW); + canvas.clipPath(clipPath); + drawBlur(canvas, AndroidUtilities.rectTmp); + canvas.restore(); + } else { + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(6), dp(6), dimPaint); + } reloadFrames(0); } else { canvas.save(); clipPath.rewind(); - AndroidUtilities.rectTmp.set(dp(16), dp(6), width + dp(20), dp(46)); + AndroidUtilities.rectTmp.set(px, top, px + width, end); clipPath.addRoundRect(AndroidUtilities.rectTmp, dp(6), dp(6), Path.Direction.CW); canvas.clipPath(clipPath); - canvas.drawRect(startX, top, endX, dp(46), paint2); + + hasBlur = frames.size() < framesToLoad; + if (!hasBlur) { + for (int a = 0; a < frames.size(); a++) { + BitmapFrame bitmap = frames.get(a); + if (bitmap.bitmap == null) { + hasBlur = true; + break; + } + } + } + if (hasBlur) { + if (customBlur()) { + AndroidUtilities.rectTmp.set(px, top, px + width + dp(4), end); + drawBlur(canvas, AndroidUtilities.rectTmp); + } else { + canvas.drawRect(startX, top, endX, end, dimPaint); + } + } int offset = 0; for (int a = 0; a < frames.size(); a++) { BitmapFrame bitmap = frames.get(a); if (bitmap.bitmap != null) { - int x = dp(16) + offset * frameWidth; - int y = dp(2 + 4); + final float x = px + offset * frameWidth; + final float y = dp(2 + 4); if (bitmap.alpha != 1f) { bitmap.alpha += 16f / 350f; if (bitmap.alpha > 1f) { @@ -504,35 +570,53 @@ 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.drawRect(px, top, startX, dp(46), dimPaint); + canvas.drawRect(endX, top, px + width, end, dimPaint); canvas.restore(); } - 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.saveLayerAlpha(0, 0, getWidth(), getHeight(), 0xFF, Canvas.ALL_SAVE_FLAG); + rect3.set(startX - dpf2(10), top, endX + dpf2(10), end); + whitePaint.setAlpha(0xFF); + canvas.drawRoundRect(rect3, dpf2(6), dpf2(6), whitePaint); + rect3.set(startX, top + dpf2(2), endX, end - dpf2(2)); + canvas.drawRect(rect3, cutPaint); canvas.restore(); - 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); + final float hw = dp(2), hh = dp(10); + float hx = startX - (dpf2(10) - hw) / 2f, hy = top + (end - top - hh) / 2f; + rect3.set(hx, hy, hx - hw, hy + hh); + canvas.drawRoundRect(rect3, dpf2(6), dpf2(6), handlePaint); - 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); + hx = endX + (dpf2(10) - hw) / 2f; + rect3.set(hx, hy, hx + hw, hy + hh); + canvas.drawRoundRect(rect3, dpf2(6), dpf2(6), handlePaint); - 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); + float loopT = loopProgress.set(0); + if (loopT > 0) { + drawProgress(canvas, progressRight, loopT); + } + drawProgress(canvas, playProgress, 1f - loopT); + } - 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 void drawProgress(Canvas canvas, float progress, float scale) { + final float px = dpf2(12); + final float width = getMeasuredWidth() - px * 2 - dp(2 * 10); + float top = dp(2); + float end = top + dp(4 + 38 + 4); + + float h = end - top; + top += h / 2f * (1f - scale); + end -= h / 2f * (1f - scale); + shadowPaint.setAlpha((int) (0x26 * scale)); + whitePaint.setAlpha((int) (0xff * scale)); + + float cx = px + dp(10) + width * progress; + rect3.set(cx - dpf2(1.5f), top, cx + dpf2(1.5f), end); + rect3.inset(-dpf2(0.66f), -dpf2(0.66f)); + canvas.drawRoundRect(rect3, dp(6), dp(6), shadowPaint); + rect3.set(cx - dpf2(1.5f), top, cx + dpf2(1.5f), end); + canvas.drawRoundRect(rect3, dp(6), dp(6), whitePaint); } private static class BitmapFrame { @@ -543,4 +627,18 @@ public BitmapFrame(Bitmap bitmap) { this.bitmap = bitmap; } } + + public void invalidateBlur() { + if (customBlur() && hasBlur) { + invalidate(); + } + } + + protected boolean customBlur() { + return false; + } + + protected void drawBlur(Canvas canvas, RectF rect) { + + } } 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 2653730a5a..93e361d51a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ViewPagerFixed.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ViewPagerFixed.java @@ -20,7 +20,6 @@ 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; @@ -57,6 +56,7 @@ public class ViewPagerFixed extends FrameLayout { private Theme.ResourcesProvider resourcesProvider; public int currentPosition; + public float currentProgress; int nextPosition; protected View[] viewPages; private int[] viewTypes; @@ -92,17 +92,18 @@ public class ViewPagerFixed extends FrameLayout { public void onAnimationUpdate(ValueAnimator valueAnimator) { if (tabsAnimationInProgress) { float scrollProgress = Math.abs(viewPages[0].getTranslationX()) / (float) viewPages[0].getMeasuredWidth(); + currentProgress = 1f - scrollProgress; if (tabsView != null) { - tabsView.selectTab(nextPosition, currentPosition, 1f - scrollProgress); + tabsView.selectTab(nextPosition, currentPosition, currentProgress); } } - onTabAnimationUpdate(); + onTabAnimationUpdate(false); } }; private Rect rect = new Rect(); private boolean allowDisallowInterceptTouch = true; - protected void onTabAnimationUpdate() { + protected void onTabAnimationUpdate(boolean manual) { } @@ -164,7 +165,15 @@ public boolean isManualScrolling() { } private ValueAnimator manualScrolling; - public void scrollToPosition(int page) { + public boolean scrollToPosition(int page) { + if (page == currentPosition || (manualScrolling != null && nextPosition == page)) { + return false; + } + if (manualScrolling != null) { + manualScrolling.cancel(); + manualScrolling = null; + } + boolean forward = currentPosition < page; animatingForward = forward; nextPosition = page; @@ -178,11 +187,6 @@ public void scrollToPosition(int page) { viewPages[1].setTranslationX(-trasnlationX); } - if (manualScrolling != null) { - manualScrolling.cancel(); - manualScrolling = null; - } - manualScrolling = ValueAnimator.ofFloat(0, 1); manualScrolling.addUpdateListener(anm -> { float progress = (float) anm.getAnimatedValue(); @@ -196,7 +200,8 @@ public void scrollToPosition(int page) { viewPages[1].setTranslationX(-viewPages[0].getMeasuredWidth() * (1f - progress)); viewPages[0].setTranslationX(viewPages[0].getMeasuredWidth() * progress); } - onTabAnimationUpdate(); + currentProgress = progress; + onTabAnimationUpdate(true); }); manualScrolling.addListener(new AnimatorListenerAdapter() { @Override @@ -209,12 +214,13 @@ public void onAnimationEnd(Animator animation) { viewPages[1] = null; } manualScrolling = null; - onTabAnimationUpdate(); + onTabAnimationUpdate(true); } }); manualScrolling.setDuration(540); manualScrolling.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); manualScrolling.start(); + return true; } public TabsView createTabsView(boolean hasStableIds, int selectorType) { @@ -387,7 +393,7 @@ private boolean prepareForMoving(MotionEvent ev, boolean forward) { viewPages[1].setTranslationX(-viewPages[0].getMeasuredWidth()); } } - onTabAnimationUpdate(); + onTabAnimationUpdate(false); return true; } @@ -471,10 +477,12 @@ public boolean onTouchEvent(MotionEvent ev) { if (viewPages[1] != null) { viewPages[1].setTranslationX(animatingForward ? viewPages[0].getMeasuredWidth() : -viewPages[0].getMeasuredWidth()); } + nextPosition = 0; + currentProgress = 0; if (tabsView != null) { tabsView.selectTab(currentPosition, 0, 0); } - onTabAnimationUpdate(); + onTabAnimationUpdate(false); } } if (maybeStartTracking && !startedTracking) { @@ -496,10 +504,11 @@ public boolean onTouchEvent(MotionEvent ev) { } } } + currentProgress = 1f - scrollProgress; if (tabsView != null) { - tabsView.selectTab(nextPosition, currentPosition, 1f - scrollProgress); + tabsView.selectTab(nextPosition, currentPosition, currentProgress); } - onTabAnimationUpdate(); + onTabAnimationUpdate(false); } } 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) { @@ -618,7 +627,7 @@ public void onAnimationEnd(Animator animator) { tabsView.setEnabled(true); } - onTabAnimationUpdate(); + onTabAnimationUpdate(false); onScrollEnd(); } }); @@ -626,7 +635,7 @@ public void onAnimationEnd(Animator animator) { tabsAnimationInProgress = true; startedTracking = false; - onTabAnimationUpdate(); + onTabAnimationUpdate(false); } else { maybeStartTracking = false; if (tabsView != null) { @@ -648,6 +657,7 @@ private void swapViews() { int p = currentPosition; currentPosition = nextPosition; nextPosition = p; + currentProgress = 1f - currentProgress; p = viewTypes[0]; viewTypes[0] = viewTypes[1]; viewTypes[1] = p; @@ -709,12 +719,14 @@ public void setPosition(int position) { if (currentPosition != position) { int oldPosition = currentPosition; currentPosition = position; + nextPosition = 0; + currentProgress = 1f; View oldView = viewPages[0]; updateViewForIndex(0); onItemSelected(viewPages[0], oldView, currentPosition, oldPosition); viewPages[0].setTranslationX(0); if (tabsView != null) { - tabsView.selectTab(position, 0, 1f); + tabsView.selectTab(currentPosition, nextPosition, currentProgress); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilerEffect2.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilerEffect2.java new file mode 100644 index 0000000000..ac65b446fd --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilerEffect2.java @@ -0,0 +1,608 @@ +package org.telegram.ui.Components.spoilers; + +import static org.telegram.messenger.AndroidUtilities.dpf2; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.SurfaceTexture; +import android.hardware.HardwareBuffer; +import android.opengl.EGL14; +import android.opengl.EGLExt; +import android.opengl.GLES20; +import android.opengl.GLES31; +import android.os.Build; +import android.util.Log; +import android.view.TextureView; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.collection.LongSparseArray; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.R; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.Utilities; +import org.telegram.ui.Components.RLottieDrawable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Objects; + +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; + +public class SpoilerEffect2 { + + public final int MAX_FPS; + private final double MIN_DELTA; + private final double MAX_DELTA; + + public static boolean supports() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; + } + + private static SpoilerEffect2 instance; + public static SpoilerEffect2 getInstance(View view) { + if (view == null || !supports()) { + return null; + } + if (instance == null) { + final int sz = getSize(); + ViewGroup rootView = getRootView(view); + if (rootView == null) { + return null; + } + instance = new SpoilerEffect2(makeTextureViewContainer(rootView), sz, sz); + } + instance.attach(view); + return instance; + } + + private static ViewGroup getRootView(View view) { + Activity activity = AndroidUtilities.findActivity(view.getContext()); + if (activity == null) { + return null; + } + View rootView = activity.findViewById(android.R.id.content).getRootView(); + if (!(rootView instanceof ViewGroup)) { + return null; + } + return (ViewGroup) rootView; + } + + public static void pause(boolean pause) { + if (instance != null && instance.thread != null) { + instance.thread.pause(pause); + } + } + + private static int getSize() { + switch (SharedConfig.getDevicePerformanceClass()) { + case SharedConfig.PERFORMANCE_CLASS_HIGH: + return Math.min(900, (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * .9f)); + case SharedConfig.PERFORMANCE_CLASS_AVERAGE: + return Math.min(512, (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * .6f)); + default: + case SharedConfig.PERFORMANCE_CLASS_LOW: + return Math.min(400, (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * .5f)); + } + } + + + private static FrameLayout makeTextureViewContainer(ViewGroup rootView) { + FrameLayout container = new FrameLayout(rootView.getContext()) { + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + return false; + } + }; + rootView.addView(container); + return container; + } + + private final ViewGroup textureViewContainer; + private final TextureView textureView; + private SpoilerThread thread; + private int width, height; + public boolean destroyed; + + private final ArrayList holders = new ArrayList(); + private final HashMap holdersToIndex = new HashMap<>(); + private int holdersIndex = 0; + + public void attach(View view) { + if (destroyed) { + return; + } + if (!holders.contains(view)) { + holders.add(view); + holdersToIndex.put(view, holdersIndex++); + } + } + + public void reassignAttach(View view, int index) { + holdersToIndex.put(view, index); + } + + public int getAttachIndex(View view) { + Integer index = holdersToIndex.get(view); + if (index == null) { + index = 0; + } + return index; + } + + public void detach(View view) { + holders.remove(view); + holdersToIndex.remove(view); + if (!destroyed) { + AndroidUtilities.cancelRunOnUIThread(checkDestroy); + AndroidUtilities.runOnUIThread(checkDestroy, 30); + } + } + + public void invalidate() { + for (int i = 0; i < holders.size(); ++i) { + holders.get(i).invalidate(); + } + } + + private final Runnable checkDestroy = () -> { + if (holders.isEmpty()) { + destroy(); + } + }; + + public void draw(Canvas canvas, View view) { + draw(canvas, view, view.getWidth(), view.getHeight(), 1f); + } + + public void draw(Canvas canvas, View view, int w, int h) { + draw(canvas, view, w, h, 1f); + } + + public void draw(Canvas canvas, View view, int w, int h, float alpha) { + if (canvas == null || view == null) { + return; + } + canvas.save(); + int ow = width, oh = height; + Integer index = holdersToIndex.get(view); + if (index == null) { + index = 0; + } + if (w > ow || h > oh) { + final float scale = Math.max(w / (float) ow, h / (float) oh); + canvas.scale(scale, scale); + } + if ((index % 4) == 1) { + canvas.rotate(180, ow / 2f, oh / 2f); + } + if ((index % 4) == 2) { + canvas.scale(-1, 1, ow / 2f, oh / 2f); + } + if ((index % 4) == 3) { + canvas.scale(1, -1, ow / 2f, oh / 2f); + } + textureView.setAlpha(alpha); + textureView.draw(canvas); + canvas.restore(); + } + + private void destroy() { + destroyed = true; + instance = null; + if (thread != null) { + thread.halt(); + thread = null; + } + textureViewContainer.removeView(textureView); + if (textureViewContainer.getParent() instanceof ViewGroup) { + ViewGroup rootView = (ViewGroup) textureViewContainer.getParent(); + rootView.removeView(textureViewContainer); + } + } + + private SpoilerEffect2(ViewGroup container, int width, int height) { + MAX_FPS = (int) AndroidUtilities.screenRefreshRate; + MIN_DELTA = 1.0 / MAX_FPS; + MAX_DELTA = MIN_DELTA * 4; + + this.width = width; + this.height = height; + + textureViewContainer = container; + textureView = new TextureView(textureViewContainer.getContext()) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(SpoilerEffect2.this.width, SpoilerEffect2.this.height); + } + }; + textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { + @Override + public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) { + if (thread == null) { + thread = new SpoilerThread(surface, width, height, SpoilerEffect2.this::invalidate); + thread.start(); + } + } + + @Override + public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) { + if (thread != null) { + thread.updateSize(width, height); + } + } + + @Override + public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) { + if (thread != null) { + thread.halt(); + thread = null; + } + return true; + } + + @Override + public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) { + + } + }); + textureView.setOpaque(false); + textureViewContainer.addView(textureView); + } + + private void resize(int w, int h) { + if (this.width == w && this.height == h) { + return; + } + this.width = w; + this.height = h; + textureView.requestLayout(); + } + + private class SpoilerThread extends Thread { + private volatile boolean running = true; + private volatile boolean paused = false; + + private final Runnable invalidate; + private final SurfaceTexture surfaceTexture; + private final Object resizeLock = new Object(); + private boolean resize; + private int width, height; + private int particlesCount; + private float radius = AndroidUtilities.dpf2(1.2f); + + public SpoilerThread(SurfaceTexture surfaceTexture, int width, int height, Runnable invalidate) { + this.invalidate = invalidate; + this.surfaceTexture = surfaceTexture; + this.width = width; + this.height = height; + this.particlesCount = particlesCount(); + } + + private int particlesCount() { + return (int) Utilities.clamp(width * height / (500f * 500f) * 1000, 10000, 500); + } + + public void updateSize(int width, int height) { + synchronized (resizeLock) { + resize = true; + this.width = width; + this.height = height; + } + } + + public void halt() { + running = false; + } + + public void pause(boolean paused) { + this.paused = paused; + } + + @Override + public void run() { + init(); + long lastTime = System.nanoTime(); + while (running) { + final long now = System.nanoTime(); + double Δt = (now - lastTime) / 1_000_000_000.; + lastTime = now; + + if (Δt < MIN_DELTA) { + double wait = MIN_DELTA - Δt; + try { + long milli = (long) (wait * 1000L); + int nano = (int) ((wait - milli / 1000.) * 1_000_000_000); + sleep(milli, nano); + } catch (Exception ignore) {} + Δt = MIN_DELTA; + } else if (Δt > MAX_DELTA) { + Δt = MAX_DELTA; + } + + while (paused) { + try { + sleep(1000); + } catch (Exception ignore) {} + } + + checkResize(); + drawFrame((float) Δt); + + AndroidUtilities.cancelRunOnUIThread(this.invalidate); + AndroidUtilities.runOnUIThread(this.invalidate); + } + die(); + } + + private EGL10 egl; + private EGLDisplay eglDisplay; + private EGLConfig eglConfig; + private EGLSurface eglSurface; + private EGLContext eglContext; + + private int drawProgram; + private int resetHandle; + private int timeHandle; + private int deltaTimeHandle; + private int sizeHandle; + private int radiusHandle; + private int seedHandle; + + private boolean reset = true; + + private int currentBuffer = 0; + private int[] particlesData; + + private void init() { + egl = (EGL10) javax.microedition.khronos.egl.EGLContext.getEGL(); + + eglDisplay = egl.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); + if (eglDisplay == egl.EGL_NO_DISPLAY) { + running = false; + return; + } + int[] version = new int[2]; + if (!egl.eglInitialize(eglDisplay, version)) { + running = false; + return; + } + + int[] configAttributes = { + EGL14.EGL_RED_SIZE, 8, + EGL14.EGL_GREEN_SIZE, 8, + EGL14.EGL_BLUE_SIZE, 8, + EGL14.EGL_ALPHA_SIZE, 8, + EGL14.EGL_RENDERABLE_TYPE, EGLExt.EGL_OPENGL_ES3_BIT_KHR, + EGL14.EGL_NONE + }; + EGLConfig[] eglConfigs = new EGLConfig[1]; + int[] numConfigs = new int[1]; + if (!egl.eglChooseConfig(eglDisplay, configAttributes, eglConfigs, 1, numConfigs)) { + running = false; + return; + } + eglConfig = eglConfigs[0]; + + int[] contextAttributes = { + EGL14.EGL_CONTEXT_CLIENT_VERSION, 3, + EGL14.EGL_NONE + }; + eglContext = egl.eglCreateContext(eglDisplay, eglConfig, egl.EGL_NO_CONTEXT, contextAttributes); + if (eglContext == null) { + running = false; + return; + } + + eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig, surfaceTexture, null); + if (eglSurface == null) { + running = false; + return; + } + + if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { + running = false; + return; + } + + genParticlesData(); + + // draw program (vertex and fragment shaders) + int vertexShader = GLES31.glCreateShader(GLES31.GL_VERTEX_SHADER); + int fragmentShader = GLES31.glCreateShader(GLES31.GL_FRAGMENT_SHADER); + if (vertexShader == 0 || fragmentShader == 0) { + running = false; + return; + } + GLES31.glShaderSource(vertexShader, RLottieDrawable.readRes(null, R.raw.spoiler_vertex) + "\n// " + Math.random()); + GLES31.glCompileShader(vertexShader); + int[] status = new int[1]; + GLES31.glGetShaderiv(vertexShader, GLES31.GL_COMPILE_STATUS, status, 0); + if (status[0] == 0) { + FileLog.e("SpoilerEffect2, compile vertex shader error: " + GLES31.glGetShaderInfoLog(vertexShader)); + GLES31.glDeleteShader(vertexShader); + running = false; + return; + } + GLES31.glShaderSource(fragmentShader, RLottieDrawable.readRes(null, R.raw.spoiler_fragment) + "\n// " + Math.random()); + GLES31.glCompileShader(fragmentShader); + GLES31.glGetShaderiv(fragmentShader, GLES31.GL_COMPILE_STATUS, status, 0); + if (status[0] == 0) { + FileLog.e("SpoilerEffect2, compile fragment shader error: " + GLES31.glGetShaderInfoLog(fragmentShader)); + GLES31.glDeleteShader(fragmentShader); + running = false; + return; + } + drawProgram = GLES31.glCreateProgram(); + if (drawProgram == 0) { + running = false; + return; + } + GLES31.glAttachShader(drawProgram, vertexShader); + GLES31.glAttachShader(drawProgram, fragmentShader); + String[] feedbackVaryings = {"outPosition", "outVelocity", "outTime", "outDuration"}; + GLES31.glTransformFeedbackVaryings(drawProgram, feedbackVaryings, GLES31.GL_INTERLEAVED_ATTRIBS); + + GLES31.glLinkProgram(drawProgram); + GLES31.glGetProgramiv(drawProgram, GLES31.GL_LINK_STATUS, status, 0); + if (status[0] == 0) { + FileLog.e("SpoilerEffect2, link draw program error: " + GLES31.glGetProgramInfoLog(drawProgram)); + running = false; + return; + } + + resetHandle = GLES31.glGetUniformLocation(drawProgram, "reset"); + timeHandle = GLES31.glGetUniformLocation(drawProgram, "time"); + deltaTimeHandle = GLES31.glGetUniformLocation(drawProgram, "deltaTime"); + sizeHandle = GLES31.glGetUniformLocation(drawProgram, "size"); + radiusHandle = GLES31.glGetUniformLocation(drawProgram, "r"); + seedHandle = GLES31.glGetUniformLocation(drawProgram, "seed"); + + GLES31.glViewport(0, 0, width, height); + GLES31.glEnable(GLES31.GL_BLEND); + GLES31.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); + GLES31.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + GLES31.glUseProgram(drawProgram); + GLES31.glUniform2f(sizeHandle, width, height); + GLES31.glUniform1f(resetHandle, reset ? 1 : 0); + GLES31.glUniform1f(radiusHandle, radius); + GLES31.glUniform1f(seedHandle, Utilities.fastRandom.nextInt(256) / 256f); + + GLES31.glUniform1f(GLES31.glGetUniformLocation(drawProgram, "noiseScale"), 6); + GLES31.glUniform1f(GLES31.glGetUniformLocation(drawProgram, "noiseSpeed"), 0.6f); + GLES31.glUniform1f(GLES31.glGetUniformLocation(drawProgram, "noiseMovement"), 4f); + GLES31.glUniform1f(GLES31.glGetUniformLocation(drawProgram, "longevity"), 1.4f); + GLES31.glUniform1f(GLES31.glGetUniformLocation(drawProgram, "dampingMult"), .9999f); + GLES31.glUniform1f(GLES31.glGetUniformLocation(drawProgram, "maxVelocity"), 6.f); + GLES31.glUniform1f(GLES31.glGetUniformLocation(drawProgram, "velocityMult"), 1.0f); + GLES31.glUniform1f(GLES31.glGetUniformLocation(drawProgram, "forceMult"), 0.6f); + } + + private float t; + private final float timeScale = .65f; + + private void drawFrame(float Δt) { + if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { + running = false; + return; + } + + t += Δt * timeScale; + if (t > 1000.f) { + t = 0; + } + + GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT); + GLES31.glBindBuffer(GLES31.GL_ARRAY_BUFFER, particlesData[currentBuffer]); + GLES31.glVertexAttribPointer(0, 2, GLES31.GL_FLOAT, false, 24, 0); // Position (vec2) + GLES31.glEnableVertexAttribArray(0); + GLES31.glVertexAttribPointer(1, 2, GLES31.GL_FLOAT, false, 24, 8); // Velocity (vec2) + GLES31.glEnableVertexAttribArray(1); + GLES31.glVertexAttribPointer(2, 1, GLES31.GL_FLOAT, false, 24, 16); // Time (float) + GLES31.glEnableVertexAttribArray(2); + GLES31.glVertexAttribPointer(3, 1, GLES31.GL_FLOAT, false, 24, 20); // Duration (float) + GLES31.glEnableVertexAttribArray(3); + GLES31.glBindBufferBase(GLES31.GL_TRANSFORM_FEEDBACK_BUFFER, 0, particlesData[1 - currentBuffer]); + GLES31.glVertexAttribPointer(0, 2, GLES31.GL_FLOAT, false, 24, 0); // Position (vec2) + GLES31.glEnableVertexAttribArray(0); + GLES31.glVertexAttribPointer(1, 2, GLES31.GL_FLOAT, false, 24, 8); // Velocity (vec2) + GLES31.glEnableVertexAttribArray(1); + GLES31.glVertexAttribPointer(2, 1, GLES31.GL_FLOAT, false, 24, 16); // Time (float) + GLES31.glEnableVertexAttribArray(2); + GLES31.glVertexAttribPointer(3, 1, GLES31.GL_FLOAT, false, 24, 20); // Duration (float) + GLES31.glEnableVertexAttribArray(3); + GLES31.glUniform1f(timeHandle, t); + GLES31.glUniform1f(deltaTimeHandle, Δt * timeScale); + GLES31.glBeginTransformFeedback(GLES31.GL_POINTS); + GLES31.glDrawArrays(GLES31.GL_POINTS, 0, particlesCount); + GLES31.glEndTransformFeedback(); + + if (reset) { + reset = false; + GLES31.glUniform1f(resetHandle, 0f); + } + currentBuffer = 1 - currentBuffer; + + egl.eglSwapBuffers(eglDisplay, eglSurface); + + checkGlErrors(); + } + + private void die() { + try { + if (particlesData != null) { + GLES31.glDeleteBuffers(2, particlesData, 0); + particlesData = null; + } + if (drawProgram != 0) { + GLES31.glDeleteProgram(drawProgram); + drawProgram = 0; + } + if (egl != null) { + egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); + egl.eglDestroySurface(eglDisplay, eglSurface); + egl.eglDestroyContext(eglDisplay, eglContext); + } + } catch (Exception e) { + FileLog.e(e); + } + try { + surfaceTexture.release(); + } catch (Exception e) { + FileLog.e(e); + } + + checkGlErrors(); + } + + private void checkResize() { + synchronized (resizeLock) { + if (resize) { + GLES31.glUniform2f(sizeHandle, width, height); + GLES31.glViewport(0, 0, width, height); + int newParticlesCount = particlesCount(); + if (newParticlesCount > this.particlesCount) { + reset = true; + genParticlesData(); + } + this.particlesCount = newParticlesCount; + resize = false; + } + } + } + + private void genParticlesData() { + if (particlesData != null) { + GLES31.glDeleteBuffers(2, particlesData, 0); + } + + particlesData = new int[2]; + GLES31.glGenBuffers(2, particlesData, 0); + + for (int i = 0; i < 2; ++i) { + GLES31.glBindBuffer(GLES31.GL_ARRAY_BUFFER, particlesData[i]); + GLES31.glBufferData(GLES31.GL_ARRAY_BUFFER, this.particlesCount * 6 * 4, null, GLES31.GL_DYNAMIC_DRAW); + } + + checkGlErrors(); + } + + private void checkGlErrors() { + int err; + while ((err = GLES31.glGetError()) != GLES31.GL_NO_ERROR) { + FileLog.e("spoiler gles error " + err); + } + } + } +} 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 0374ea202b..a77f671d1b 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 @@ -44,13 +44,16 @@ public class CellFlickerDrawable { Runnable onRestartCallback; public CellFlickerDrawable() { - this(64, 204); + this(64, 204, 160); } - public CellFlickerDrawable(int a1, int a2) { - size = AndroidUtilities.dp(160); - gradientShader = new LinearGradient(0, 0, size, 0, new int[]{Color.TRANSPARENT, ColorUtils.setAlphaComponent(Color.WHITE, a1), Color.TRANSPARENT}, null, Shader.TileMode.CLAMP); - gradientShader2 = new LinearGradient(0, 0, size, 0, new int[]{Color.TRANSPARENT, ColorUtils.setAlphaComponent(Color.WHITE, a2), Color.TRANSPARENT}, null, Shader.TileMode.CLAMP); + public CellFlickerDrawable(int a1, int a) { + this(a1, a, 160); + } + public CellFlickerDrawable(int a1, int a2, int size) { + this.size = AndroidUtilities.dp(size); + gradientShader = new LinearGradient(0, 0, this.size, 0, new int[]{Color.TRANSPARENT, ColorUtils.setAlphaComponent(Color.WHITE, a1), Color.TRANSPARENT}, null, Shader.TileMode.CLAMP); + gradientShader2 = new LinearGradient(0, 0, this.size, 0, new int[]{Color.TRANSPARENT, ColorUtils.setAlphaComponent(Color.WHITE, a2), Color.TRANSPARENT}, null, Shader.TileMode.CLAMP); paint.setShader(gradientShader); paintOutline.setShader(gradientShader2); paintOutline.setStyle(Paint.Style.STROKE); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DefaultThemesPreviewCell.java b/TMessagesProj/src/main/java/org/telegram/ui/DefaultThemesPreviewCell.java index e8bd1c9468..980426c61c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DefaultThemesPreviewCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DefaultThemesPreviewCell.java @@ -290,7 +290,7 @@ public void onAnimationEnd(Animator animation) { ArrayList themes = new ArrayList<>(MediaDataController.getInstance(parentFragment.getCurrentAccount()).defaultEmojiThemes); if (currentType == ThemeActivity.THEME_TYPE_BASIC) { - EmojiThemes chatTheme = EmojiThemes.createPreviewCustom(); + EmojiThemes chatTheme = EmojiThemes.createPreviewCustom(parentFragment.getCurrentAccount()); chatTheme.loadPreviewColors(parentFragment.getCurrentAccount()); ChatThemeBottomSheet.ChatThemeItem item = new ChatThemeBottomSheet.ChatThemeItem(chatTheme); item.themeIndex = !Theme.isCurrentThemeDay() ? 2 : 0; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java index 0f0bd9171c..8c09055109 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java @@ -45,7 +45,6 @@ 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; @@ -156,6 +155,7 @@ import org.telegram.ui.Cells.ShadowSectionCell; import org.telegram.ui.Cells.TextCell; import org.telegram.ui.Cells.TextInfoPrivacyCell; +import org.telegram.ui.Cells.UnconfirmedAuthHintCell; import org.telegram.ui.Cells.UserCell; import org.telegram.ui.Components.AlertsCreator; import org.telegram.ui.Components.AnimatedEmojiDrawable; @@ -211,7 +211,6 @@ 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 org.telegram.ui.Stories.UserListPoller; import org.telegram.ui.Stories.recorder.HintView2; import org.telegram.ui.Stories.recorder.StoryRecorder; @@ -219,6 +218,7 @@ import java.io.File; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.Random; @@ -268,6 +268,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. private float storiesOverscroll; private boolean storiesOverscrollCalled; private boolean wasDrawn; + private int fragmentContextTopPadding; public MessagesStorage.TopicKey getOpenedDialogId() { return openedDialogId; @@ -459,7 +460,10 @@ public void updateList(boolean animated) { private FragmentContextView fragmentLocationContextView; private FragmentContextView fragmentContextView; private DialogsHintCell dialogsHintCell; + private UnconfirmedAuthHintCell authHintCell; + private float authHintCellProgress; private boolean dialogsHintCellVisible; + private boolean authHintCellVisible; private Long cacheSize, deviceSize; private ArrayList frozenDialogsList; @@ -669,16 +673,8 @@ private boolean prepareForMoving(MotionEvent ev, boolean forward) { @Override public void setPadding(int left, int top, int right, int bottom) { - topPadding = top; - updateContextViewPosition(); - if (rightSlidingDialogContainer != null) { - rightSlidingDialogContainer.setFragmentViewPadding(topPadding); - } - if (whiteActionBar && searchViewPager != null) { - searchViewPager.setTranslationY(topPadding - lastMeasuredTopPadding + searchViewPagerTranslationY); - } else { - requestLayout(); - } + fragmentContextTopPadding = top; + updateTopPadding(); } public boolean checkTabsAnimationInProgress() { @@ -753,7 +749,7 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { return true; } boolean result; - if (child == viewPages[0] || (viewPages.length > 1 && child == viewPages[1]) || child == fragmentContextView || child == fragmentLocationContextView || child == dialogsHintCell) { + if (child == viewPages[0] || (viewPages.length > 1 && child == viewPages[1]) || child == fragmentContextView || child == fragmentLocationContextView || child == dialogsHintCell || child == authHintCell) { canvas.save(); canvas.clipRect(0, -getY() + getActionBarTop() + getActionBarFullHeight(), getMeasuredWidth(), getMeasuredHeight()); @@ -969,6 +965,10 @@ protected void dispatchDraw(Canvas canvas) { } } + if (parentLayout != null && authHintCell != null && authHintCell.getVisibility() == View.VISIBLE) { + parentLayout.drawHeaderShadow(canvas, (int) (0xFF * (1f - searchAnimationProgress) * authHintCell.getAlpha()), (int) (authHintCell.getBottom() + authHintCell.getTranslationY())); + } + if (fragmentContextView != null && fragmentContextView.isCallStyle()) { canvas.save(); canvas.translate(fragmentContextView.getX(), fragmentContextView.getY()); @@ -1079,6 +1079,9 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (dialogsHintCell != null && dialogsHintCell.getVisibility() == View.VISIBLE) { h += dialogsHintCell.getMeasuredHeight(); } + if (authHintCell != null && authHintCell.getVisibility() == View.VISIBLE) { + h += authHintCell.getMeasuredHeight(); + } } } else if (!onlySelect || initialDialogsType == DIALOGS_TYPE_FORWARD) { h -= actionBar.getMeasuredHeight(); @@ -1233,7 +1236,7 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { if (dialogsHintCell != null) { childTop += dialogsHintCell.height(); } - } else if (child instanceof DialogsHintCell || child instanceof FragmentContextView) { + } else if (child == dialogsHintCell || child instanceof FragmentContextView || child == authHintCell) { childTop += actionBar.getMeasuredHeight(); } else if (dialogStoriesCell != null && dialogStoriesCell.getPremiumHint() == child) { continue; @@ -1370,7 +1373,7 @@ public boolean onTouchEvent(MotionEvent ev) { dispatchTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0)); filterTabsView.shakeLock(viewPages[1].selectedType); AndroidUtilities.runOnUIThread(() -> { - showDialog(new LimitReachedBottomSheet(DialogsActivity.this, getContext(), LimitReachedBottomSheet.TYPE_FOLDERS, currentAccount)); + showDialog(new LimitReachedBottomSheet(DialogsActivity.this, getContext(), LimitReachedBottomSheet.TYPE_FOLDERS, currentAccount, null)); }, 200); return false; } else { @@ -1556,12 +1559,28 @@ protected void onDetachedFromWindow() { } } + private void updateTopPadding() { + topPadding = fragmentContextTopPadding; + updateContextViewPosition(); + if (rightSlidingDialogContainer != null) { + rightSlidingDialogContainer.setFragmentViewPadding(topPadding); + } + if (whiteActionBar && searchViewPager != null) { + searchViewPager.setTranslationY(topPadding - lastMeasuredTopPadding + searchViewPagerTranslationY); + } else { + fragmentView.requestLayout(); + } + } + 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); + if (progressToActionMode == 1f) { + p = 1f; + } float pHalf = Utilities.clamp(p / 0.5f, 1f, 0f); dialogStoriesCell.setClipTop(0); if (!hasStories && animateToHasStories) { @@ -1593,12 +1612,12 @@ private void updateStoriesViewAlpha(float alpha) { float s = 0.8f + 0.2f * containersAlpha; actionBar.getTitlesContainer().setScaleY(s); actionBar.getTitlesContainer().setScaleX(s); - actionBar.getTitlesContainer().setAlpha(containersAlpha); + actionBar.getTitlesContainer().setAlpha(containersAlpha * (1f - progressToActionMode)); } else { actionBar.getTitlesContainer().setScaleY(1f); actionBar.getTitlesContainer().setScaleY(1f); actionBar.getTitlesContainer().setScaleX(1f); - actionBar.getTitlesContainer().setAlpha(1f); + actionBar.getTitlesContainer().setAlpha(1f - progressToActionMode); } } @@ -1970,6 +1989,9 @@ protected void onMeasure(int widthSpec, int heightSpec) { if (hasStories && !actionModeFullyShowed) { t += AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP); } + if (authHintCell != null && authHintCellProgress != 0) { + t += authHintCell.getMeasuredHeight() * authHintCellProgress; + } setTopGlowOffset(t); setPadding(0, t, 0, 0); if (hasStories) { @@ -2677,6 +2699,7 @@ public boolean onFragmentCreate() { getNotificationCenter().addObserver(this, NotificationCenter.onDatabaseReset); getNotificationCenter().addObserver(this, NotificationCenter.storiesUpdated); getNotificationCenter().addObserver(this, NotificationCenter.storiesEnabledUpdate); + getNotificationCenter().addObserver(this, NotificationCenter.unconfirmedAuthUpdate); if (initialDialogsType == DIALOGS_TYPE_DEFAULT) { getNotificationCenter().addObserver(this, NotificationCenter.chatlistFolderUpdate); @@ -2713,7 +2736,7 @@ public static void loadDialogs(AccountInstance accountInstance) { messagesController.loadHintDialogs(); messagesController.loadUserInfo(accountInstance.getUserConfig().getCurrentUser(), false, 0); accountInstance.getContactsController().checkInviteText(); - accountInstance.getMediaDataController().chekAllMedia(false); + accountInstance.getMediaDataController().checkAllMedia(false); AndroidUtilities.runOnUIThread(() -> accountInstance.getDownloadController().loadDownloadingFiles(), 200); for (String emoji : messagesController.diceEmojies) { accountInstance.getMediaDataController().loadStickersByEmojiOrName(emoji, true, true); @@ -2814,6 +2837,7 @@ public void onFragmentDestroy() { getNotificationCenter().removeObserver(this, NotificationCenter.onDatabaseReset); getNotificationCenter().removeObserver(this, NotificationCenter.storiesUpdated); getNotificationCenter().removeObserver(this, NotificationCenter.storiesEnabledUpdate); + getNotificationCenter().removeObserver(this, NotificationCenter.unconfirmedAuthUpdate); if (initialDialogsType == DIALOGS_TYPE_DEFAULT) { getNotificationCenter().removeObserver(this, NotificationCenter.chatlistFolderUpdate); @@ -2906,6 +2930,10 @@ public View createView(final Context context) { AndroidUtilities.runOnUIThread(() -> Theme.createChatResources(context, false)); + authHintCellVisible = false; + authHintCellProgress = 0f; + authHintCell = null; + ActionBarMenu menu = actionBar.createMenu(); doneItem = new ActionBarMenuItem(context, null, Theme.getColor(Theme.key_actionBarDefaultSelector), Theme.getColor(Theme.key_actionBarDefaultIcon), true); doneItem.setText(LocaleController.getString("Done", R.string.Done).toUpperCase()); @@ -3287,7 +3315,7 @@ public void onPageSelected(FilterTabsView.Tab tab, boolean forward) { } if (tab.isLocked) { filterTabsView.shakeLock(tab.id); - showDialog(new LimitReachedBottomSheet(DialogsActivity.this, context, LimitReachedBottomSheet.TYPE_FOLDERS, currentAccount)); + showDialog(new LimitReachedBottomSheet(DialogsActivity.this, context, LimitReachedBottomSheet.TYPE_FOLDERS, currentAccount, null)); return; } @@ -4392,6 +4420,7 @@ public void onLongClickRelease() { finishPreviewFragment(); return; } + Bundle args = new Bundle(); args.putBoolean("destroyAfterSelect", true); presentFragment(new ContactsActivity(args)); @@ -4462,7 +4491,7 @@ public void getOutline(View view, Outline outline) { final StoriesController.StoryLimit storyLimit = MessagesController.getInstance(currentAccount).getStoriesController().checkStoryLimit(); if (storyLimit != null) { - showDialog(new LimitReachedBottomSheet(this, getContext(), storyLimit.getLimitReachedType(), currentAccount)); + showDialog(new LimitReachedBottomSheet(this, getContext(), storyLimit.getLimitReachedType(), currentAccount, null)); return; } @@ -4889,40 +4918,39 @@ public void onUserLongPressed(View view, long dialogId) { 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)); - }) - .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(); - }).makeMultiline(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(); - }).makeMultiline(false) - .addIf(!isArchive(), R.drawable.msg_archive, LocaleController.getString("ArchivePeerStories", R.string.ArchivePeerStories), () -> { - toggleArciveForStory(dialogId); - }).makeMultiline(false) - .addIf(isArchive(), R.drawable.msg_unarchive, LocaleController.getString("UnarchiveStories", R.string.UnarchiveStories), () -> { - toggleArciveForStory(dialogId); - }).makeMultiline(false); + .addIf(dialogId > 0, R.drawable.msg_discussion, LocaleController.getString("SendMessage", R.string.SendMessage), () -> { + presentFragment(ChatActivity.of(dialogId)); + }) + .addIf(dialogId > 0, R.drawable.msg_openprofile, LocaleController.getString("OpenProfile", R.string.OpenProfile), () -> { + presentFragment(ProfileActivity.of(dialogId)); + }) + .addIf(dialogId < 0, R.drawable.msg_channel, LocaleController.getString("OpenChannel2", R.string.OpenChannel2), () -> { + presentFragment(ChatActivity.of(dialogId)); + }).addIf(!muted && dialogId > 0, 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(); + }).makeMultiline(false).addIf(muted && dialogId > 0, 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(); + }).makeMultiline(false).addIf(!isArchive(), R.drawable.msg_archive, LocaleController.getString("ArchivePeerStories", R.string.ArchivePeerStories), () -> { + toggleArciveForStory(dialogId); + }).makeMultiline(false).addIf(isArchive(), R.drawable.msg_unarchive, LocaleController.getString("UnarchiveStories", R.string.UnarchiveStories), () -> { + toggleArciveForStory(dialogId); + }).makeMultiline(false); } filterOptions.setGravity(Gravity.LEFT) .translate(AndroidUtilities.dp(-8), AndroidUtilities.dp(-10)) @@ -5260,7 +5288,6 @@ 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); @@ -5269,13 +5296,25 @@ private void toggleArciveForStory(long dialogId) { getMessagesController().getStoriesController().toggleHidden(dialogId, hide, true, true); }; CharSequence str; + String name; + TLObject object; + if (dialogId >= 0) { + TLRPC.User user = getMessagesController().getUser(dialogId); + name = ContactsController.formatName(user.first_name, null, 15); + object = user; + } else { + TLRPC.Chat chat = getMessagesController().getChat(-dialogId); + name = chat.title; + object = chat; + } + if (isArchive()) { - str = AndroidUtilities.replaceTags(LocaleController.formatString("StoriesMovedToDialogs", R.string.StoriesMovedToDialogs, ContactsController.formatName(user.first_name, null, 15))); + str = AndroidUtilities.replaceTags(LocaleController.formatString("StoriesMovedToDialogs", R.string.StoriesMovedToDialogs, name)); } else { - str = AndroidUtilities.replaceTags(LocaleController.formatString("StoriesMovedToContacts", R.string.StoriesMovedToContacts, ContactsController.formatName(user.first_name, null, 15))); + str = AndroidUtilities.replaceTags(LocaleController.formatString("StoriesMovedToContacts", R.string.StoriesMovedToContacts, ContactsController.formatName(name, null, 15))); } storiesBulletin = BulletinFactory.global().createUsersBulletin( - Arrays.asList(user), + Collections.singletonList(object), str, null, undoObject).show(); @@ -5312,6 +5351,9 @@ private float getActionBarMoveFrom(boolean showFilterTabs) { if (dialogsHintCell != null && dialogsHintCell.getVisibility() == View.VISIBLE) { h += dialogsHintCell.getMeasuredHeight(); } + if (authHintCell != null && authHintCellVisible) { + h += authHintCell.getMeasuredHeight(); + } return h; } @@ -5465,11 +5507,67 @@ private void updateCommentView() { commentViewPreviousTop = top; } + private ValueAnimator authHintCellAnimator; + private void updateAuthHintCellVisibility(boolean visible) { + if (authHintCellVisible != visible) { + authHintCellVisible = visible; + if (authHintCell == null) { + return; + } + if (authHintCellAnimator != null) { + authHintCellAnimator.cancel(); + authHintCellAnimator = null; + } + if (visible) { + authHintCell.setVisibility(View.VISIBLE); + } + authHintCell.setAlpha(1f); + viewPages[0].listView.requestLayout(); + if (fragmentView != null) { + fragmentView.requestLayout(); + } + notificationsLocker.lock(); + ValueAnimator valueAnimator = ValueAnimator.ofFloat(authHintCellProgress, visible ? 1f : 0); + valueAnimator.addUpdateListener(animation -> { + authHintCellProgress = (float) animation.getAnimatedValue(); + updateContextViewPosition(); + viewPages[0].listView.requestLayout(); + }); + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + notificationsLocker.unlock(); + if (fragmentView != null) { + fragmentView.requestLayout(); + } + authHintCellProgress = visible ? 1f : 0; + if (!visible) { + authHintCell.setVisibility(View.GONE); + } + } + }); + valueAnimator.setDuration(250); + valueAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + valueAnimator.start(); + } + } + private void updateDialogsHint() { - if (dialogsHintCell == null) { + if (dialogsHintCell == null || getContext() == null) { return; } - if (isPremiumRestoreHintVisible()) { + if (!getMessagesController().getUnconfirmedAuthController().auths.isEmpty() && folderId == 0 && initialDialogsType == DIALOGS_TYPE_DEFAULT) { + dialogsHintCellVisible = false; + dialogsHintCell.setVisibility(View.GONE); + if (authHintCell == null) { + authHintCell = new UnconfirmedAuthHintCell(getContext()); + if (fragmentView instanceof ContentView) { + ((ContentView) fragmentView).addView(authHintCell); + } + } + authHintCell.set(DialogsActivity.this, currentAccount); + updateAuthHintCellVisibility(true); + } else if (isPremiumRestoreHintVisible()) { dialogsHintCellVisible = true; dialogsHintCell.setVisibility(View.VISIBLE); dialogsHintCell.setOnClickListener(v -> { @@ -5488,6 +5586,7 @@ private void updateDialogsHint() { ), LocaleController.getString(R.string.RestorePremiumHintMessage) ); + updateAuthHintCellVisibility(false); } else if (isPremiumHintVisible()) { dialogsHintCellVisible = true; dialogsHintCell.setVisibility(View.VISIBLE); @@ -5507,6 +5606,7 @@ private void updateDialogsHint() { ), LocaleController.getString(isPremiumHintUpgrade ? R.string.UpgradePremiumMessage : R.string.SaveOnAnnualPremiumMessage) ); + updateAuthHintCellVisibility(false); } else if (isCacheHintVisible()) { dialogsHintCellVisible = true; dialogsHintCell.setVisibility(View.VISIBLE); @@ -5526,9 +5626,11 @@ private void updateDialogsHint() { ), LocaleController.getString(R.string.ClearStorageHintMessage) ); + updateAuthHintCellVisibility(false); } else { dialogsHintCellVisible = false; dialogsHintCell.setVisibility(View.GONE); + updateAuthHintCellVisibility(false); } } @@ -5747,6 +5849,14 @@ private void updateContextViewPosition() { dialogsHintCell.setTranslationY(totalOffset); totalOffset += dialogsHintCell.getMeasuredHeight(); } + if (authHintCell != null) { + if (rightSlidingDialogContainer != null && rightSlidingDialogContainer.hasFragment()) { + totalOffset -= authHintCell.getMeasuredHeight() * rightSlidingDialogContainer.openedProgress; + } + float authHintCellTranslation = authHintCell.getMeasuredHeight() * (1f - authHintCellProgress); + authHintCell.setTranslationY(-authHintCellTranslation + totalOffset); + totalOffset += authHintCellTranslation; + } if (fragmentContextView != null) { float from = 0; if (fragmentLocationContextView != null && fragmentLocationContextView.getVisibility() == View.VISIBLE) { @@ -6046,7 +6156,7 @@ public boolean processQr(String text, Runnable onLoadEnd) { } int totalCount = currentCount + alwaysShow.size(); if ((totalCount > getMessagesController().dialogFiltersChatsLimitDefault && !getUserConfig().isPremium()) || totalCount > getMessagesController().dialogFiltersChatsLimitPremium) { - showDialog(new LimitReachedBottomSheet(DialogsActivity.this, fragmentView.getContext(), LimitReachedBottomSheet.TYPE_CHATS_IN_FOLDER, currentAccount)); + showDialog(new LimitReachedBottomSheet(DialogsActivity.this, fragmentView.getContext(), LimitReachedBottomSheet.TYPE_CHATS_IN_FOLDER, currentAccount, null)); return; } if (filter != null) { @@ -6526,6 +6636,7 @@ public int getTopOffset(int tag) { (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) + + (authHintCell != null && authHintCellVisible ? authHintCell.getHeight() : 0) + (dialogStoriesCell != null && dialogStoriesCellVisible ? (int) ((1f - dialogStoriesCell.getCollapsedProgress()) * AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP)) : 0) ); } @@ -7160,6 +7271,16 @@ public void setSearchAnimationProgress(float progress, boolean full) { } } } + if (authHintCell != null) { + authHintCell.setAlpha(1f - progress); + if (authHintCellVisible) { + if (authHintCell.getAlpha() == 0) { + authHintCell.setVisibility(View.INVISIBLE); + } else { + authHintCell.setVisibility(View.VISIBLE); + } + } + } final boolean budget = SharedConfig.getDevicePerformanceClass() == SharedConfig.PERFORMANCE_CLASS_LOW || !LiteMode.isEnabled(LiteMode.FLAG_CHAT_SCALE); if (full) { if (viewPages[0] != null) { @@ -8252,8 +8373,10 @@ public void onAnimationEnd(Animator animation) { actionBarColorAnimator = null; actionModeFullyShowed = false; if (hasStories) { + invalidateScrollY = true; + fixScrollYAfterArchiveOpened = true; + fragmentView.invalidate(); scrollAdditionalOffset = -(AndroidUtilities.dp(DialogStoriesCell.HEIGHT_IN_DP) - finalTranslateListHeight); - viewPages[0].setTranslationY(0); for (int i = 0; i < viewPages.length; i++) { if (viewPages[i] != null) { @@ -8423,7 +8546,7 @@ private void performSelectedDialogsAction(ArrayList selectedDialogs, int a if (folderId != 0 || filter != null) { AlertsCreator.showSimpleAlert(DialogsActivity.this, LocaleController.formatString("PinFolderLimitReached", R.string.PinFolderLimitReached, LocaleController.formatPluralString("Chats", maxPinnedCount))); } else { - LimitReachedBottomSheet limitReachedBottomSheet = new LimitReachedBottomSheet(this, getParentActivity(), LimitReachedBottomSheet.TYPE_PIN_DIALOGS, currentAccount); + LimitReachedBottomSheet limitReachedBottomSheet = new LimitReachedBottomSheet(this, getParentActivity(), LimitReachedBottomSheet.TYPE_PIN_DIALOGS, currentAccount, null); showDialog(limitReachedBottomSheet); } return; @@ -9249,6 +9372,7 @@ public void onAnimationEnd(Animator animation) { viewPages[i].listView.requestLayout(); } } + dialogStoriesCell.setProgressToCollapse(1f, false); fragmentView.requestLayout(); } } @@ -10062,6 +10186,8 @@ public void onAnimationEnd(Animator animation) { updateStoriesVisibility(wasDrawn); } else if (id == NotificationCenter.storiesEnabledUpdate) { updateStoriesPosting(); + } else if (id == NotificationCenter.unconfirmedAuthUpdate) { + updateDialogsHint(); } } @@ -11597,6 +11723,9 @@ public ArrayList getThemeDescriptions() { if (dialogsHintCell != null) { SimpleThemeDescription.add(arrayList, dialogsHintCell::updateColors, Theme.key_windowBackgroundWhite, Theme.key_windowBackgroundWhiteBlackText, Theme.key_windowBackgroundWhiteGrayText); } + if (authHintCell != null) { + SimpleThemeDescription.add(arrayList, authHintCell::updateColors, Theme.key_windowBackgroundWhite, Theme.key_windowBackgroundWhiteBlackText, Theme.key_windowBackgroundWhiteGrayText, Theme.key_windowBackgroundWhiteValueText, Theme.key_text_RedBold); + } return arrayList; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/EmojiAnimationsOverlay.java b/TMessagesProj/src/main/java/org/telegram/ui/EmojiAnimationsOverlay.java index f3a285c277..59466c2261 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/EmojiAnimationsOverlay.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/EmojiAnimationsOverlay.java @@ -6,12 +6,15 @@ import android.view.View; import android.widget.FrameLayout; +import androidx.annotation.Nullable; + import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.Emoji; import org.telegram.messenger.EmojiData; +import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.ImageLocation; import org.telegram.messenger.ImageReceiver; @@ -28,7 +31,10 @@ import org.telegram.tgnet.TLRPC; import org.telegram.ui.Cells.ChatActionCell; import org.telegram.ui.Cells.ChatMessageCell; +import org.telegram.ui.Components.AnimatedEmojiDrawable; import org.telegram.ui.Components.Bulletin; +import org.telegram.ui.Components.Reactions.AnimatedEmojiEffect; +import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.Components.StickerSetBulletinLayout; import org.telegram.ui.Components.StickersAlert; @@ -45,6 +51,7 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe private final int ANIMATION_JSON_VERSION = 1; private final String INTERACTIONS_STICKER_PACK = "EmojiAnimations"; + @Nullable ChatActivity chatActivity; int currentAccount; TLRPC.TL_messages_stickerSet set; @@ -86,6 +93,10 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe long dialogId; int threadMsgId; + public EmojiAnimationsOverlay(FrameLayout frameLayout, int currentAccount) { + this.contentLayout = frameLayout; + this.currentAccount = currentAccount; + } public EmojiAnimationsOverlay(ChatActivity chatActivity, FrameLayout frameLayout, RecyclerListView chatListView, int currentAccount, long dialogId, int threadMsgId) { this.chatActivity = chatActivity; @@ -96,19 +107,43 @@ public EmojiAnimationsOverlay(ChatActivity chatActivity, FrameLayout frameLayout this.threadMsgId = threadMsgId; } - protected void onAttachedToWindow() { + public void onAttachedToWindow() { attached = true; checkStickerPack(); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.diceStickersDidLoad); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.onEmojiInteractionsReceived); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.updateInterfaces); + for (int i = 0; i < drawingObjects.size(); i++) { + drawingObjects.get(i).imageReceiver.onAttachedToWindow(); + if (drawingObjects.get(i).genericEffect != null) { + drawingObjects.get(i).genericEffect.setView(contentLayout); + } + } } - protected void onDetachedFromWindow() { + public void onDetachedFromWindow() { attached = false; NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.diceStickersDidLoad); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.onEmojiInteractionsReceived); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.updateInterfaces); + for (int i = 0; i < drawingObjects.size(); i++) { + drawingObjects.get(i).imageReceiver.onDetachedFromWindow(); + if (drawingObjects.get(i).genericEffect != null) { + drawingObjects.get(i).genericEffect.removeView(contentLayout); + } + } + drawingObjects.clear(); + } + + + public void clear() { + for (int i = 0; i < drawingObjects.size(); i++) { + drawingObjects.get(i).imageReceiver.onDetachedFromWindow(); + if (drawingObjects.get(i).genericEffect != null) { + drawingObjects.get(i).genericEffect.removeView(contentLayout); + } + } + drawingObjects.clear(); } public void checkStickerPack() { @@ -161,6 +196,9 @@ public void didReceivedNotification(int id, int account, Object... args) { } else if (id == NotificationCenter.onEmojiInteractionsReceived) { if (NekoConfig.disableRemoteEmojiInteractions.Bool()) return; + if (chatActivity == null) { + return; + } long dialogId = (long) args[0]; TLRPC.TL_sendMessageEmojiInteraction action = (TLRPC.TL_sendMessageEmojiInteraction) args[1]; if (dialogId == this.dialogId && supportedEmoji.contains(action.emoticon)) { @@ -220,7 +258,7 @@ private void findViewAndShowAnimation(int messageId, int animation) { } } - if (bestView != null) { + if (bestView != null && chatActivity != null) { chatActivity.restartSticker(bestView); if (!EmojiData.hasEmojiSupportVibration(bestView.getMessageObject().getStickerEmoji()) && !bestView.getMessageObject().isPremiumSticker() && !bestView.getMessageObject().isAnimatedAnimatedEmoji()) { if (!NekoConfig.disableVibration.Bool()) @@ -234,88 +272,125 @@ public void draw(Canvas canvas) { if (!drawingObjects.isEmpty()) { for (int i = 0; i < drawingObjects.size(); i++) { DrawingObject drawingObject = drawingObjects.get(i); - drawingObject.viewFound = false; - float childY = 0; - for (int k = 0; k < listView.getChildCount(); k++) { - View child = listView.getChildAt(k); - ImageReceiver photoImage = null; - MessageObject messageObject = null; - if (child instanceof ChatMessageCell) { - ChatMessageCell cell = (ChatMessageCell) child; - messageObject = cell.getMessageObject(); - photoImage = cell.getPhotoImage(); - } else if (child instanceof ChatActionCell) { - ChatActionCell cell = (ChatActionCell) child; - messageObject = cell.getMessageObject(); - photoImage = cell.getPhotoImage(); - } - if (messageObject != null && messageObject.getId() == drawingObject.messageId) { - drawingObject.viewFound = true; - float viewX = listView.getX() + child.getX(); - float viewY = listView.getY() + child.getY(); - childY = child.getY(); - if (drawingObject.isPremiumSticker) { - drawingObject.lastX = viewX + photoImage.getImageX(); - drawingObject.lastY = viewY + photoImage.getImageY(); - } else { - viewX += photoImage.getImageX(); - viewY += photoImage.getImageY(); - if (drawingObject.isOut) { - viewX += -photoImage.getImageWidth() * 2 + AndroidUtilities.dp(24); + if (chatActivity != null) { + drawingObject.viewFound = false; + float childY = 0; + for (int k = 0; k < listView.getChildCount(); k++) { + View child = listView.getChildAt(k); + ImageReceiver photoImage = null; + MessageObject messageObject = null; + if (child instanceof ChatMessageCell) { + ChatMessageCell cell = (ChatMessageCell) child; + messageObject = cell.getMessageObject(); + photoImage = cell.getPhotoImage(); + } else if (child instanceof ChatActionCell) { + ChatActionCell cell = (ChatActionCell) child; + messageObject = cell.getMessageObject(); + photoImage = cell.getPhotoImage(); + } + if (messageObject != null && messageObject.getId() == drawingObject.messageId) { + drawingObject.viewFound = true; + float viewX = listView.getX() + child.getX(); + float viewY = listView.getY() + child.getY(); + childY = child.getY(); + if (drawingObject.isPremiumSticker) { + drawingObject.lastX = viewX + photoImage.getImageX(); + drawingObject.lastY = viewY + photoImage.getImageY(); } else { - viewX += -AndroidUtilities.dp(24); + viewX += photoImage.getImageX(); + viewY += photoImage.getImageY(); + if (drawingObject.isOut) { + viewX += -photoImage.getImageWidth() * 2 + AndroidUtilities.dp(24); + } else { + viewX += -AndroidUtilities.dp(24); + } + viewY -= photoImage.getImageWidth(); + drawingObject.lastX = viewX; + drawingObject.lastY = viewY; } - viewY -= photoImage.getImageWidth(); - drawingObject.lastX = viewX; - drawingObject.lastY = viewY; + drawingObject.lastW = photoImage.getImageWidth(); + drawingObject.lastH = photoImage.getImageHeight(); + break; } - drawingObject.lastW = photoImage.getImageWidth(); - drawingObject.lastH = photoImage.getImageHeight(); - break; } - } - if (!drawingObject.viewFound || childY + drawingObject.lastH < chatActivity.getChatListViewPadding() || childY > listView.getMeasuredHeight() - chatActivity.blurredViewBottomOffset) { - drawingObject.removing = true; - } + if (!drawingObject.viewFound || childY + drawingObject.lastH < chatActivity.getChatListViewPadding() || childY > listView.getMeasuredHeight() - chatActivity.blurredViewBottomOffset) { + drawingObject.removing = true; + } - if (drawingObject.removing && drawingObject.removeProgress != 1f) { - drawingObject.removeProgress = Utilities.clamp(drawingObject.removeProgress + 16 / 150f, 1f, 0); - drawingObject.imageReceiver.setAlpha(1f - drawingObject.removeProgress); - chatActivity.contentView.invalidate(); - } + if (drawingObject.isPremiumSticker) { + float halfStickerHeight = drawingObject.lastH / 2f; + boolean outsideDown = listView.getMeasuredHeight() - childY <= halfStickerHeight; + boolean outsideUp = childY - chatActivity.getChatListViewPadding() + halfStickerHeight <= 0; + if (outsideDown || outsideUp) { + drawingObject.removing = true; + } + } - if (drawingObject.isPremiumSticker) { - float size = drawingObject.lastH * 1.49926f; - float paddingHorizontal = size * 0.0546875f; - float centerY = drawingObject.lastY + drawingObject.lastH / 2f; - float top = centerY - size / 2f - size * 0.00279f; - if (!drawingObject.isOut) { - drawingObject.imageReceiver.setImageCoords(drawingObject.lastX - paddingHorizontal, top, size, size); - } else { - drawingObject.imageReceiver.setImageCoords(drawingObject.lastX + drawingObject.lastW - size + paddingHorizontal, top, size, size); + if (drawingObject.removing && drawingObject.removeProgress != 1f) { + drawingObject.removeProgress = Utilities.clamp(drawingObject.removeProgress + 16 / 150f, 1f, 0); + drawingObject.imageReceiver.setAlpha(1f - drawingObject.removeProgress); + chatActivity.contentView.invalidate(); } - if (!drawingObject.isOut) { - canvas.save(); - canvas.scale(-1f, 1, drawingObject.imageReceiver.getCenterX(), drawingObject.imageReceiver.getCenterY()); - drawingObject.imageReceiver.draw(canvas); - canvas.restore(); + } + + boolean removeOnStart = !drawingObject.wasPlayed && drawingObject.removing; + if (!removeOnStart) { + if (drawingObject.isPremiumSticker) { + float size = drawingObject.lastH * 1.49926f; + float paddingHorizontal = size * 0.0546875f; + float centerY = drawingObject.lastY + drawingObject.lastH / 2f; + float top = centerY - size / 2f - size * 0.00279f; + if (!drawingObject.isOut) { + drawingObject.imageReceiver.setImageCoords(drawingObject.lastX - paddingHorizontal, top, size, size); + } else { + drawingObject.imageReceiver.setImageCoords(drawingObject.lastX + drawingObject.lastW - size + paddingHorizontal, top, size, size); + } + + if (!drawingObject.isOut) { + canvas.save(); + canvas.scale(-1f, 1, drawingObject.imageReceiver.getCenterX(), drawingObject.imageReceiver.getCenterY()); + drawingObject.imageReceiver.draw(canvas); + canvas.restore(); + } else { + drawingObject.imageReceiver.draw(canvas); + } } else { - drawingObject.imageReceiver.draw(canvas); + if (drawingObject.genericEffect != null) { + float x = drawingObject.lastX + drawingObject.randomOffsetX; + float y = drawingObject.lastY + drawingObject.randomOffsetY; + float size = drawingObject.lastW * 3; + drawingObject.genericEffect.setBounds((int) x, (int) y, (int) (x + size), (int) (y + size)); + drawingObject.genericEffect.draw(canvas); + } else { + drawingObject.imageReceiver.setImageCoords(drawingObject.lastX + drawingObject.randomOffsetX, drawingObject.lastY + drawingObject.randomOffsetY, drawingObject.lastW * 3, drawingObject.lastW * 3); + if (!drawingObject.isOut) { + canvas.save(); + canvas.scale(-1f, 1, drawingObject.imageReceiver.getCenterX(), drawingObject.imageReceiver.getCenterY()); + drawingObject.imageReceiver.draw(canvas); + canvas.restore(); + } else { + drawingObject.imageReceiver.draw(canvas); + } + } } + } + + boolean isDone; + if (drawingObject.genericEffect != null) { + isDone = drawingObject.genericEffect.isDone(); } else { - drawingObject.imageReceiver.setImageCoords(drawingObject.lastX + drawingObject.randomOffsetX, drawingObject.lastY + drawingObject.randomOffsetY, drawingObject.lastW * 3, drawingObject.lastW * 3); - if (!drawingObject.isOut) { - canvas.save(); - canvas.scale(-1f, 1, drawingObject.imageReceiver.getCenterX(), drawingObject.imageReceiver.getCenterY()); - drawingObject.imageReceiver.draw(canvas); - canvas.restore(); - } else { - drawingObject.imageReceiver.draw(canvas); - } + isDone = (drawingObject.wasPlayed && drawingObject.imageReceiver.getLottieAnimation() != null && drawingObject.imageReceiver.getLottieAnimation().getCurrentFrame() >= drawingObject.imageReceiver.getLottieAnimation().getFramesCount() - 2); } - if (drawingObject.removeProgress == 1f || (drawingObject.wasPlayed && drawingObject.imageReceiver.getLottieAnimation() != null && drawingObject.imageReceiver.getLottieAnimation().getCurrentFrame() >= drawingObject.imageReceiver.getLottieAnimation().getFramesCount() - 2)) { - drawingObjects.remove(i); + if (drawingObject.removeProgress == 1f || isDone || removeOnStart) { + DrawingObject toRemove = drawingObjects.remove(i); + if (drawingObject.isPremiumSticker && drawingObject.imageReceiver.getLottieAnimation() != null) { + toRemove.imageReceiver.getLottieAnimation().setCurrentFrame(0, true, true); + } + toRemove.imageReceiver.onDetachedFromWindow(); + if (toRemove.genericEffect != null) { + toRemove.genericEffect.removeView(contentLayout); + } i--; } else if (drawingObject.imageReceiver.getLottieAnimation() != null && drawingObject.imageReceiver.getLottieAnimation().isRunning()) { drawingObject.wasPlayed = true; @@ -432,7 +507,7 @@ public boolean showAnimationForActionCell(ChatActionCell view, TLRPC.Document do drawingObject.messageId = view.getMessageObject().getId(); drawingObject.isOut = true; drawingObject.imageReceiver.setAllowStartAnimation(true); - int w = (int) (1.5f * imageW / AndroidUtilities.density); + int w = getFilterWidth(); if (sameAnimationsCountDocumentId > 0) { Integer lastIndex = lastAnimationIndex.get(document.id); int currentIndex = lastIndex == null ? 0 : lastIndex; @@ -451,8 +526,10 @@ public boolean showAnimationForActionCell(ChatActionCell view, TLRPC.Document do drawingObject.imageReceiver.getLottieAnimation().start(); } drawingObjects.add(drawingObject); - drawingObject.imageReceiver.onAttachedToWindow(); - drawingObject.imageReceiver.setParentView(contentLayout); + if (attached) { + drawingObject.imageReceiver.onAttachedToWindow(); + drawingObject.imageReceiver.setParentView(contentLayout); + } contentLayout.invalidate(); return true; } @@ -474,15 +551,15 @@ public void preloadAnimation(ChatMessageCell cell) { if (arrayList == null || arrayList.isEmpty()) { return; } - int size = (int) (2f * cell.getPhotoImage().getImageWidth() / AndroidUtilities.density); int preloadCount = Math.min(1, arrayList.size()); for (int i = 0; i < preloadCount; ++i) { - this.preloadAnimation(arrayList.get(i), size); + this.preloadAnimation(arrayList.get(i)); } } private HashMap preloaded; - private void preloadAnimation(TLRPC.Document document, int size) { + + private void preloadAnimation(TLRPC.Document document) { if (document == null) { return; } @@ -493,7 +570,7 @@ private void preloadAnimation(TLRPC.Document document, int size) { preloaded = new HashMap<>(); } preloaded.put(document.id, true); - new ImageReceiver().setImage(ImageLocation.getForDocument(document), size + "_" + size, null, "tgs", set, 1); + MediaDataController.getInstance(currentAccount).preloadImage(ImageLocation.getForDocument(document), FileLoader.PRIORITY_NORMAL_UP); } private boolean showAnimationForCell(ChatMessageCell view, int animation, boolean sendTap, boolean sendSeen) { @@ -518,21 +595,28 @@ private boolean showAnimationForCell(ChatMessageCell view, int animation, boolea } emoji = unwrapEmoji(emoji); - boolean isPremiumSticker = messageObject.isPremiumSticker(); + int viewId = view.getMessageObject().getId(); + TLRPC.Document viewDocument = view.getMessageObject().getDocument(); + boolean isOutOwner = view.getMessageObject().isOutOwner(); + return createDrawingObject(emoji, viewId, viewDocument, messageObject, animation, sendTap, sendSeen, imageW, imageH, isOutOwner); + } + + private boolean createDrawingObject(String emoji, int viewId, TLRPC.Document viewDocument, @Nullable MessageObject messageObject, int animation, boolean sendTap, boolean sendSeen, float imageW, float imageH, boolean isOutOwner) { + boolean isPremiumSticker = messageObject != null && messageObject.isPremiumSticker(); if (supportedEmoji.contains(emoji) || isPremiumSticker) { ArrayList arrayList = emojiInteractionsStickersMap.get(emoji); if ((arrayList != null && !arrayList.isEmpty()) || isPremiumSticker) { int sameAnimationsCountMessageId = 0; int sameAnimationsCountDocumentId = 0; for (int i = 0; i < drawingObjects.size(); i++) { - if (drawingObjects.get(i).messageId == view.getMessageObject().getId()) { + if (drawingObjects.get(i).messageId == viewId) { sameAnimationsCountMessageId++; if (drawingObjects.get(i).imageReceiver.getLottieAnimation() == null || drawingObjects.get(i).imageReceiver.getLottieAnimation().isGeneratingCache()) { return false; } } - if (drawingObjects.get(i).document != null && view.getMessageObject().getDocument() != null && drawingObjects.get(i).document.id == view.getMessageObject().getDocument().id) { + if (drawingObjects.get(i).document != null && viewDocument != null && drawingObjects.get(i).document.id == viewDocument.id) { sameAnimationsCountDocumentId++; } } @@ -566,7 +650,7 @@ private boolean showAnimationForCell(ChatMessageCell view, int animation, boolea TLRPC.VideoSize videoSize = null; if (isPremiumSticker) { videoSize = messageObject.getPremiumStickerAnimation(); - } else if (messageObject.isAnimatedAnimatedEmoji()) { + } else if (messageObject != null && messageObject.isAnimatedAnimatedEmoji()) { if (animation < 0 || animation > arrayList.size() - 1) { ArrayList preloadedVariants = new ArrayList<>(); for (int i = 0; i < arrayList.size(); ++i) { @@ -598,20 +682,21 @@ private boolean showAnimationForCell(ChatMessageCell view, int animation, boolea } DrawingObject drawingObject = new DrawingObject(); - drawingObject.isPremiumSticker = messageObject.isPremiumSticker(); + drawingObject.isPremiumSticker = messageObject == null ? false : messageObject.isPremiumSticker(); drawingObject.randomOffsetX = imageW / 4 * ((random.nextInt() % 101) / 100f); drawingObject.randomOffsetY = imageH / 4 * ((random.nextInt() % 101) / 100f); - drawingObject.messageId = view.getMessageObject().getId(); + drawingObject.messageId = viewId; drawingObject.document = document; - drawingObject.isOut = view.getMessageObject().isOutOwner(); + drawingObject.isOut = isOutOwner; drawingObject.imageReceiver.setAllowStartAnimation(true); drawingObject.imageReceiver.setAllowLottieVibration(sendTap); int w; if (document != null) { - w = (int) (2f * imageW / AndroidUtilities.density); + w = getFilterWidth(); Integer lastIndex = lastAnimationIndex.get(document.id); - int currentIndex = ((lastIndex == null ? 0 : lastIndex) + 1) % 4; + int currentIndex = (lastIndex == null ? 0 : lastIndex) + 1; lastAnimationIndex.put(document.id, currentIndex); + //currentIndex = currentIndex % 4; ImageLocation imageLocation = ImageLocation.getForDocument(document); drawingObject.imageReceiver.setUniqKeyPrefix(currentIndex + "_" + drawingObject.messageId + "_"); @@ -619,12 +704,14 @@ private boolean showAnimationForCell(ChatMessageCell view, int animation, boolea drawingObject.imageReceiver.setImage(imageLocation, w + "_" + w + "_pcache_compress", null, "tgs", set, 1); drawingObject.imageReceiver.setDelegate(new ImageReceiver.ImageReceiverDelegate() { @Override - public void didSetImage(ImageReceiver imageReceiver, boolean set, boolean thumb, boolean memCache) {} + public void didSetImage(ImageReceiver imageReceiver, boolean set, boolean thumb, boolean memCache) { + } + @Override public void onAnimationReady(ImageReceiver imageReceiver) { if (sendTap && messageObject.isAnimatedAnimatedEmoji() && imageReceiver.getLottieAnimation() != null && !imageReceiver.getLottieAnimation().hasVibrationPattern()) { if (!NekoConfig.disableVibration.Bool()) - view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + contentLayout.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); } } }); @@ -632,7 +719,7 @@ public void onAnimationReady(ImageReceiver imageReceiver) { drawingObject.imageReceiver.getLottieAnimation().setCurrentFrame(0, false, true); } } else { - w = (int) (1.5f * imageW / AndroidUtilities.density); + w = getFilterWidth(); if (sameAnimationsCountDocumentId > 0) { Integer lastIndex = lastAnimationIndex.get(messageObject.getDocument().id); int currentIndex = lastIndex == null ? 0 : lastIndex; @@ -657,13 +744,13 @@ public void onAnimationReady(ImageReceiver imageReceiver) { contentLayout.invalidate(); if (sendTap && !isPremiumSticker && UserConfig.getInstance(currentAccount).clientUserId != dialogId) { - if (lastTappedMsgId != 0 && lastTappedMsgId != view.getMessageObject().getId()) { + if (lastTappedMsgId != 0 && lastTappedMsgId != viewId) { if (sentInteractionsRunnable != null) { AndroidUtilities.cancelRunOnUIThread(sentInteractionsRunnable); sentInteractionsRunnable.run(); } } - lastTappedMsgId = view.getMessageObject().getId(); + lastTappedMsgId = viewId; lastTappedEmoji = emoji; if (lastTappedTime == 0) { lastTappedTime = System.currentTimeMillis(); @@ -694,7 +781,20 @@ public void onAnimationReady(ImageReceiver imageReceiver) { return false; } + public static int getFilterWidth() { + int w; + if (AndroidUtilities.isTablet()) { + w = (int) (AndroidUtilities.getMinTabletSide() * 0.4f); + } else { + w = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.5f); + } + return (int) (2f * w / AndroidUtilities.density); + } + private void showStickerSetBulletin(TLRPC.TL_messages_stickerSet stickerSet, MessageObject messageObject) { + if (chatActivity == null) { + return; + } if (MessagesController.getInstance(currentAccount).premiumLocked || chatActivity.getParentActivity() == null) { return; } @@ -800,11 +900,33 @@ public boolean checkPosition(ChatMessageCell messageCell, float chatListViewPadd } public void cancelAllAnimations() { - for (int i = 0; i < drawingObjects.size(); i++) { drawingObjects.get(i).removing = true; } + } + + public void setAccount(int currentAccount) { + this.currentAccount = currentAccount; + } + + public void preload(ReactionsLayoutInBubble.VisibleReaction visibleReaction) { + String emoji = visibleReaction.emojicon; + if (emoji == null) { + TLRPC.Document document = AnimatedEmojiDrawable.findDocument(currentAccount, visibleReaction.documentId); + emoji = MessageObject.findAnimatedEmojiEmoticon(document); + } + if (emoji == null) { + return; + } + ArrayList arrayList = emojiInteractionsStickersMap.get(emoji); + if (arrayList == null || arrayList.isEmpty()) { + return; + } + int preloadCount = Math.min(1, arrayList.size()); + for (int i = 0; i < preloadCount; ++i) { + this.preloadAnimation(arrayList.get(i)); + } } private class DrawingObject { @@ -816,6 +938,9 @@ private class DrawingObject { public float randomOffsetX; public float randomOffsetY; public boolean isPremiumSticker; + public boolean isReaction; + public AnimatedEmojiEffect genericEffect; + public long documentId; boolean wasPlayed; boolean isOut; boolean removing; @@ -823,6 +948,11 @@ private class DrawingObject { int messageId; TLRPC.Document document; ImageReceiver imageReceiver = new ImageReceiver(); + + DrawingObject() { + imageReceiver.setAllowLoadingOnAttachedOnly(true); + imageReceiver.setAllowDrawWhileCacheGenerating(true); + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ExternalActionActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ExternalActionActivity.java index 10f7ce9026..b5d195cdf0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ExternalActionActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ExternalActionActivity.java @@ -91,7 +91,7 @@ protected void onCreate(Bundle savedInstanceState) { SharedConfig.lastPauseTime = (int) (SystemClock.elapsedRealtime() / 1000); } - AndroidUtilities.fillStatusBarHeight(this); + AndroidUtilities.fillStatusBarHeight(this, false); Theme.createDialogsResources(this); Theme.createChatResources(this, false); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/FilterChatlistActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/FilterChatlistActivity.java index 5c9d2f7239..8208745f6f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/FilterChatlistActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/FilterChatlistActivity.java @@ -193,7 +193,7 @@ public boolean requestFocus(int direction, Rect previouslyFocusedRect) { ((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)); + showDialog(new LimitReachedBottomSheet(this, getContext(), LimitReachedBottomSheet.TYPE_CHATS_IN_FOLDER, currentAccount, null)); return; } selectedPeers.add(did); @@ -337,11 +337,11 @@ private void save() { updateDoneProgress(false); saving = false; if (err != null && "INVITES_TOO_MUCH".equals(err.text)) { - showDialog(new LimitReachedBottomSheet(this, getContext(), LimitReachedBottomSheet.TYPE_FOLDER_INVITES, currentAccount)); + showDialog(new LimitReachedBottomSheet(this, getContext(), LimitReachedBottomSheet.TYPE_FOLDER_INVITES, currentAccount, null)); } else if (err != null && "INVITE_PEERS_TOO_MUCH".equals(err.text)) { - showDialog(new LimitReachedBottomSheet(this, getContext(), LimitReachedBottomSheet.TYPE_CHATS_IN_FOLDER, currentAccount)); + showDialog(new LimitReachedBottomSheet(this, getContext(), LimitReachedBottomSheet.TYPE_CHATS_IN_FOLDER, currentAccount, null)); } else if (err != null && "CHATLISTS_TOO_MUCH".equals(err.text)) { - showDialog(new LimitReachedBottomSheet(this, getContext(), LimitReachedBottomSheet.TYPE_SHARED_FOLDERS, currentAccount)); + showDialog(new LimitReachedBottomSheet(this, getContext(), LimitReachedBottomSheet.TYPE_SHARED_FOLDERS, currentAccount, null)); } else { finishFragment(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/FilterCreateActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/FilterCreateActivity.java index a3e5861814..1b1c3fbb8e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/FilterCreateActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/FilterCreateActivity.java @@ -512,7 +512,7 @@ private void onClickCreateLink(View view) { final int maxCount = getUserConfig().isPremium() ? getMessagesController().dialogFiltersChatsLimitPremium : getMessagesController().dialogFiltersChatsLimitDefault; if (peers.size() > maxCount) { - showDialog(new LimitReachedBottomSheet(this, getContext(), LimitReachedBottomSheet.TYPE_CHATS_IN_FOLDER, currentAccount)); + showDialog(new LimitReachedBottomSheet(this, getContext(), LimitReachedBottomSheet.TYPE_CHATS_IN_FOLDER, currentAccount, null)); return; } @@ -1988,6 +1988,13 @@ public NewSpan(boolean outline) { } } + public NewSpan(float textSize) { + this.outline = false; + textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + bgPaint.setStyle(Paint.Style.FILL); + textPaint.setTextSize(dp(textSize)); + } + public void setColor(int color) { this.color = color; } @@ -2453,23 +2460,23 @@ public static boolean processErrors(TLRPC.TL_error err, BaseFragment fragment, B return true; } if ("INVITE_PEERS_TOO_MUCH".equals(err.text)) { - new LimitReachedBottomSheet(fragment, fragment.getContext(), LimitReachedBottomSheet.TYPE_CHATS_IN_FOLDER, fragment.getCurrentAccount()).show(); + new LimitReachedBottomSheet(fragment, fragment.getContext(), LimitReachedBottomSheet.TYPE_CHATS_IN_FOLDER, fragment.getCurrentAccount(), null).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(); + new LimitReachedBottomSheet(fragment, fragment.getContext(), LimitReachedBottomSheet.TYPE_TO0_MANY_COMMUNITIES, fragment.getCurrentAccount(), null).show(); } else if ("INVITES_TOO_MUCH".equals(err.text)) { - new LimitReachedBottomSheet(fragment, fragment.getContext(), LimitReachedBottomSheet.TYPE_FOLDER_INVITES, fragment.getCurrentAccount()).show(); + new LimitReachedBottomSheet(fragment, fragment.getContext(), LimitReachedBottomSheet.TYPE_FOLDER_INVITES, fragment.getCurrentAccount(), null).show(); } else if ("CHATLISTS_TOO_MUCH".equals(err.text)) { - new LimitReachedBottomSheet(fragment, fragment.getContext(), LimitReachedBottomSheet.TYPE_SHARED_FOLDERS, fragment.getCurrentAccount()).show(); + new LimitReachedBottomSheet(fragment, fragment.getContext(), LimitReachedBottomSheet.TYPE_SHARED_FOLDERS, fragment.getCurrentAccount(), null).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(); + new LimitReachedBottomSheet(fragment, fragment.getContext(), LimitReachedBottomSheet.TYPE_CHATS_IN_FOLDER, fragment.getCurrentAccount(), null).show(); } else if ("DIALOG_FILTERS_TOO_MUCH".equals(err.text)) { - new LimitReachedBottomSheet(fragment, fragment.getContext(), LimitReachedBottomSheet.TYPE_FOLDERS, fragment.getCurrentAccount()).show(); + new LimitReachedBottomSheet(fragment, fragment.getContext(), LimitReachedBottomSheet.TYPE_FOLDERS, fragment.getCurrentAccount(), null).show(); } else { factory.createErrorBulletin(LocaleController.getString("UnknownError", R.string.UnknownError)).show(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/FiltersSetupActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/FiltersSetupActivity.java index 5c27b9b68f..cedfa1b8bc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/FiltersSetupActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/FiltersSetupActivity.java @@ -613,7 +613,7 @@ protected void dispatchDraw(Canvas canvas) { return; } if (filter.locked) { - showDialog(new LimitReachedBottomSheet(this, context, LimitReachedBottomSheet.TYPE_FOLDERS, currentAccount)); + showDialog(new LimitReachedBottomSheet(this, context, LimitReachedBottomSheet.TYPE_FOLDERS, currentAccount, null)); } else { presentFragment(new FilterCreateActivity(filter)); } @@ -623,7 +623,7 @@ protected void dispatchDraw(Canvas canvas) { count - 1 >= getMessagesController().dialogFiltersLimitDefault && !getUserConfig().isPremium() || count >= getMessagesController().dialogFiltersLimitPremium ) { - showDialog(new LimitReachedBottomSheet(this, context, LimitReachedBottomSheet.TYPE_FOLDERS, currentAccount)); + showDialog(new LimitReachedBottomSheet(this, context, LimitReachedBottomSheet.TYPE_FOLDERS, currentAccount, null)); } else { presentFragment(new FilterCreateActivity()); } @@ -791,7 +791,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType 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)); + showDialog(new LimitReachedBottomSheet(FiltersSetupActivity.this, mContext, LimitReachedBottomSheet.TYPE_FOLDERS, currentAccount, null)); } else { presentFragment(new FilterCreateActivity(filter)); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java index fcb0bd458b..b40cf82825 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java @@ -28,6 +28,7 @@ import android.os.Build; import android.os.Bundle; import android.text.InputFilter; +import android.text.TextUtils; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -278,6 +279,61 @@ protected boolean hideKeyboardOnShow() { return false; } + private void setDefaultGroupName() { + TLRPC.User currentUser = getUserConfig().getCurrentUser(); + int members = selectedContacts.size() + 1; + if (members >= 2 && members <= 5 && TextUtils.isEmpty(editText.getText())) { + String txt = ""; + try { + switch (members) { + case 2: + txt = LocaleController.formatString( + "GroupCreateMembersTwo", R.string.GroupCreateMembersTwo, + currentUser.first_name, + getFirstNameByPos(0) + ); + break; + case 3: + txt = LocaleController.formatString( + "GroupCreateMembersThree", R.string.GroupCreateMembersThree, + currentUser.first_name, + getFirstNameByPos(0), + getFirstNameByPos(1) + ); + break; + case 4: + txt = LocaleController.formatString( + "GroupCreateMembersFour", R.string.GroupCreateMembersFour, + currentUser.first_name, + getFirstNameByPos(0), + getFirstNameByPos(1), + getFirstNameByPos(2) + ); + break; + case 5: + txt = LocaleController.formatString( + "GroupCreateMembersFive", R.string.GroupCreateMembersFive, + currentUser.first_name, + getFirstNameByPos(0), + getFirstNameByPos(1), + getFirstNameByPos(2), + getFirstNameByPos(3) + ); + break; + } + } catch (Exception e) { + FileLog.e(e); + } + if (!TextUtils.isEmpty(txt)) { + editText.setText(txt); + } + } + } + + private String getFirstNameByPos(int pos) { + return getMessagesController().getUser(selectedContacts.get(pos)).first_name; + } + @Override public View createView(Context context) { if (editText != null) { @@ -544,6 +600,7 @@ public void setAlpha(float alpha) { editText.setText(nameToSet); nameToSet = null; } + setDefaultGroupName(); InputFilter[] inputFilters = new InputFilter[1]; inputFilters[0] = new InputFilter.LengthFilter(100); editText.setFilters(inputFilters); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java index 59760508cb..ca7a65caf2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java @@ -20,6 +20,7 @@ import android.app.ActivityManager; import android.app.Dialog; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; @@ -54,7 +55,6 @@ 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; @@ -190,6 +190,7 @@ import org.telegram.ui.Components.ThemeEditorView; import org.telegram.ui.Components.UndoView; import org.telegram.ui.Components.UpdateAppAlertDialog; +import org.telegram.ui.Components.spoilers.SpoilerEffect2; import org.telegram.ui.Components.voip.VoIPHelper; import org.telegram.ui.Stories.StoriesController; import org.telegram.ui.Stories.StoriesListPlaceProvider; @@ -408,7 +409,7 @@ protected void onCreate(Bundle savedInstanceState) { if (SharedConfig.passcodeHash.length() != 0 && SharedConfig.appLocked) { SharedConfig.lastPauseTime = (int) (SystemClock.elapsedRealtime() / 1000); } - AndroidUtilities.fillStatusBarHeight(this); + AndroidUtilities.fillStatusBarHeight(this, false); actionBarLayout = INavigationLayout.newLayout(this); frameLayout = new FrameLayout(this) { @@ -567,13 +568,26 @@ public boolean drawChild(Canvas canvas, View child, long drawingTime) { } else if (!UserConfig.hasPremiumOnAccounts()) { if (actionBarLayout.getFragmentStack().size() > 0) { BaseFragment fragment = actionBarLayout.getFragmentStack().get(0); - LimitReachedBottomSheet limitReachedBottomSheet = new LimitReachedBottomSheet(fragment, this, TYPE_ACCOUNTS, currentAccount); + LimitReachedBottomSheet limitReachedBottomSheet = new LimitReachedBottomSheet(fragment, this, TYPE_ACCOUNTS, currentAccount, null); fragment.showDialog(limitReachedBottomSheet); limitReachedBottomSheet.onShowPremiumScreenRunnable = () -> drawerLayoutContainer.closeDrawer(false); } } } else { int id = drawerLayoutAdapter.getId(position); + TLRPC.TL_attachMenuBot attachMenuBot = drawerLayoutAdapter.getAttachMenuBot(position); + if (attachMenuBot != null) { + if (attachMenuBot.side_menu_disclaimer_needed) { + WebAppDisclaimerAlert.show(this, (ignore) -> { + attachMenuBot.side_menu_disclaimer_needed = false; + showAttachMenuBot(attachMenuBot); + MediaDataController.getInstance(currentAccount).updateAttachMenuBotsInCache(); + }, null); + } else { + showAttachMenuBot(attachMenuBot); + } + return; + } if (id == 2) { Bundle args = new Bundle(); presentFragment(new GroupCreateActivity(args)); @@ -765,6 +779,14 @@ public void onPreviewOpenAnimationEnd() { return true; } } + if (view instanceof DrawerActionCell) { + int id = drawerLayoutAdapter.getId(position); + TLRPC.TL_attachMenuBot attachMenuBot = drawerLayoutAdapter.getAttachMenuBot(position); + if (attachMenuBot != null) { + BotWebViewSheet.deleteBot(currentAccount, attachMenuBot.bot_id, null); + return true; + } + } return false; }); drawerLayoutContainer.setParentActionBarLayout(actionBarLayout); @@ -979,6 +1001,14 @@ public void onViewDetachedFromWindow(View v) { RestrictedLanguagesSelectActivity.checkRestrictedLanguages(false); } + private void showAttachMenuBot(TLRPC.TL_attachMenuBot attachMenuBot) { + BotWebViewSheet webViewSheet = new BotWebViewSheet(this, getLastFragment().getResourceProvider()); + webViewSheet.setParentActivity(this); + webViewSheet.requestWebView(currentAccount, attachMenuBot.bot_id, attachMenuBot.bot_id, attachMenuBot.short_name, null, BotWebViewSheet.TYPE_SIMPLE_WEB_VIEW_BUTTON, 0, false, BotWebViewSheet.FLAG_FROM_SIDE_MENU); + webViewSheet.show(); + drawerLayoutContainer.closeDrawer(); + } + @Override public void onThemeProgress(float progress) { if (ArticleViewer.hasInstance() && ArticleViewer.getInstance().isVisible()) { @@ -1432,6 +1462,7 @@ private void checkCurrentAccount() { if (currentAccount != UserConfig.selectedAccount) { NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.appDidLogout); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.mainUserInfoChanged); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.attachMenuBotsDidLoad); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.didUpdateConnectionState); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.needShowAlert); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.wasUnableToFindCurrentLocation); @@ -1452,6 +1483,7 @@ private void checkCurrentAccount() { currentAccount = UserConfig.selectedAccount; NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.appDidLogout); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.mainUserInfoChanged); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.attachMenuBotsDidLoad); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.didUpdateConnectionState); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.needShowAlert); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.wasUnableToFindCurrentLocation); @@ -2895,17 +2927,20 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool if (push_topic_id > 0) { TLRPC.TL_forumTopic topic = MessagesController.getInstance(currentAccount).getTopicsController().findTopic(push_chat_id, push_topic_id); - FileLog.d(push_chat_id + " " + push_topic_id + " TL_forumTopic " + topic); + FileLog.d("LaunchActivity openForum " + push_chat_id + " " + push_topic_id + " TL_forumTopic " + topic); if (topic != null) { - TLRPC.Message message = topic.topicStartMessage; - ArrayList messageObjects = new ArrayList<>(); - TLRPC.Chat chatLocal = MessagesController.getInstance(currentAccount).getChat(push_chat_id); - messageObjects.add(new MessageObject(currentAccount, message, false, false)); - fragment.setThreadMessages(messageObjects, chatLocal, topic.id, topic.read_inbox_max_id, topic.read_outbox_max_id, topic); + ForumUtilities.applyTopic(fragment, MessagesStorage.TopicKey.of(-push_chat_id, push_topic_id)); } else { boolean finalIsNew = isNew; + long finalPush_chat_id = push_chat_id; + int finalPush_topic_id = push_topic_id; MessagesController.getInstance(currentAccount).getTopicsController().loadTopic(push_chat_id, push_topic_id, () -> { - handleIntent(intent, finalIsNew, restore, fromPassword, progress); + TLRPC.TL_forumTopic loadedTopic = MessagesController.getInstance(currentAccount).getTopicsController().findTopic(finalPush_chat_id, finalPush_topic_id); + FileLog.d("LaunchActivity openForum after load " + finalPush_chat_id + " " + finalPush_topic_id + " TL_forumTopic " + loadedTopic); + if (actionBarLayout != null) { + ForumUtilities.applyTopic(fragment, MessagesStorage.TopicKey.of(-finalPush_chat_id, finalPush_topic_id)); + actionBarLayout.presentFragment(fragment); + } }); return true; } @@ -3753,46 +3788,6 @@ private void runLinkRequest(final int intentAccount, }); } 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) { - TLRPC.TL_messages_getBotApp getBotApp = new TLRPC.TL_messages_getBotApp(); - TLRPC.TL_inputBotAppShortName app = new TLRPC.TL_inputBotAppShortName(); - app.bot_id = MessagesController.getInstance(currentAccount).getInputUser(user); - 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, 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(()->{ - dismissLoading.run(); - - AtomicBoolean allowWrite = new AtomicBoolean(); - BaseFragment lastFragment = mainFragmentsStack.get(mainFragmentsStack.size() - 1); - Runnable loadBotSheet = ()->{ - BotWebViewSheet sheet = new BotWebViewSheet(LaunchActivity.this, lastFragment.getResourceProvider()); - sheet.setParentActivity(LaunchActivity.this); - sheet.requestWebView(currentAccount, user.id, user.id, null, null, BotWebViewSheet.TYPE_WEB_VIEW_BOT_APP, 0, false, lastFragment, botApp.app, allowWrite.get(), botAppStartParam, user); - sheet.show(); - }; - - if (botApp.inactive || forceNotInternalForApps) { - AlertsCreator.createBotLaunchAlert(lastFragment, botApp, user, allowWrite, loadBotSheet); - } else { - loadBotSheet.run(); - } - }); - } - }); - return; - } - } - - if (setAsAttachBot != null && attachMenuBotToOpen == null) { TLRPC.User user = MessagesController.getInstance(intentAccount).getUser(peerId); if (user != null && user.bot) { if (user.bot_attach_menu) { @@ -3800,129 +3795,62 @@ private void runLinkRequest(final int intentAccount, getAttachMenuBot.bot = MessagesController.getInstance(intentAccount).getInputUser(peerId); ConnectionsManager.getInstance(intentAccount).sendRequest(getAttachMenuBot, (response1, error1) -> AndroidUtilities.runOnUIThread(() -> { if (response1 instanceof TLRPC.TL_attachMenuBotsBot) { - TLRPC.TL_attachMenuBotsBot attachMenuBotsBot = (TLRPC.TL_attachMenuBotsBot) response1; - MessagesController.getInstance(intentAccount).putUsers(attachMenuBotsBot.users, false); - TLRPC.TL_attachMenuBot attachMenuBot = attachMenuBotsBot.bot; - BaseFragment lastFragment_ = mainFragmentsStack.get(mainFragmentsStack.size() - 1); - if (AndroidUtilities.isTablet() && !(lastFragment_ instanceof ChatActivity) && !rightFragmentsStack.isEmpty()) { - lastFragment_ = rightFragmentsStack.get(rightFragmentsStack.size() - 1); + processAttachMenuBot(intentAccount, peerId, attachMenuBotChoose, user, setAsAttachBot); + if (progress != null) { + progress.end(); } - final BaseFragment lastFragment = lastFragment_; + AndroidUtilities.runOnUIThread(() -> { + dismissLoading.run(); + }); + } + })); + } else { + TLRPC.TL_messages_getBotApp getBotApp = new TLRPC.TL_messages_getBotApp(); + TLRPC.TL_inputBotAppShortName app = new TLRPC.TL_inputBotAppShortName(); + app.bot_id = MessagesController.getInstance(intentAccount).getInputUser(user); + app.short_name = botAppMaybe; + getBotApp.app = app; + ConnectionsManager.getInstance(intentAccount).sendRequest(getBotApp, (response1, error1) -> { + if (progress != null) { + progress.end(); + } + if (error1 != null) { + AndroidUtilities.runOnUIThread(() -> 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, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, null, null, progress, forceNotInternalForApps, storyId)); + } else { + TLRPC.TL_messages_botApp botApp = (TLRPC.TL_messages_botApp) response1; + AndroidUtilities.runOnUIThread(() -> { + dismissLoading.run(); - List chooserTargets = new ArrayList<>(); - if (!TextUtils.isEmpty(attachMenuBotChoose)) { - for (String target : attachMenuBotChoose.split(" ")) { - if (MediaDataController.canShowAttachMenuBotForTarget(attachMenuBot, target)) { - chooserTargets.add(target); - } - } - } - DialogsActivity dialogsActivity; - - if (!chooserTargets.isEmpty()) { - Bundle args = new Bundle(); - args.putInt("dialogsType", DialogsActivity.DIALOGS_TYPE_START_ATTACH_BOT); - 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")); - - dialogsActivity = new DialogsActivity(args); - dialogsActivity.setDelegate((fragment, dids, message1, param, topicsFragment) -> { - long did = dids.get(0).dialogId; - - Bundle args1 = new Bundle(); - args1.putBoolean("scrollToTopOnResume", true); - if (DialogObject.isEncryptedDialog(did)) { - args1.putInt("enc_id", DialogObject.getEncryptedChatId(did)); - } else if (DialogObject.isUserDialog(did)) { - args1.putLong("user_id", did); - } else { - args1.putLong("chat_id", -did); - } - args1.putString("attach_bot", UserObject.getPublicUsername(user)); - if (setAsAttachBot != null) { - args1.putString("attach_bot_start_command", setAsAttachBot); - } - if (MessagesController.getInstance(intentAccount).checkCanOpenChat(args1, fragment)) { - NotificationCenter.getInstance(intentAccount).postNotificationName(NotificationCenter.closeChats); - actionBarLayout.presentFragment(new ChatActivity(args1), true, false, true, false); + AtomicBoolean allowWrite = new AtomicBoolean(); + BaseFragment lastFragment = mainFragmentsStack.get(mainFragmentsStack.size() - 1); + Runnable loadBotSheet = () -> { + BotWebViewSheet sheet = new BotWebViewSheet(LaunchActivity.this, lastFragment.getResourceProvider()); + sheet.setParentActivity(LaunchActivity.this); + sheet.requestWebView(intentAccount, user.id, user.id, null, null, BotWebViewSheet.TYPE_WEB_VIEW_BOT_APP, 0, false, lastFragment, botApp.app, allowWrite.get(), botAppStartParam, user); + sheet.show(); + if (botApp.inactive || forceNotInternalForApps) { + sheet.showJustAddedBulletin(); } - return true; - }); - } else { - dialogsActivity = null; - } + }; - if (!attachMenuBot.inactive) { - if (dialogsActivity != null) { - presentFragment(dialogsActivity); - } else if (lastFragment instanceof ChatActivity) { - ChatActivity chatActivity = (ChatActivity) lastFragment; - if (!MediaDataController.canShowAttachMenuBot(attachMenuBot, chatActivity.getCurrentUser() != null ? chatActivity.getCurrentUser() : chatActivity.getCurrentChat())) { - BulletinFactory.of(lastFragment).createErrorBulletin(LocaleController.getString(R.string.BotAlreadyAddedToAttachMenu)).show(); - return; - } - chatActivity.openAttachBotLayout(user.id, setAsAttachBot); + if (botApp.inactive || forceNotInternalForApps) { + AlertsCreator.createBotLaunchAlert(lastFragment, botApp, user, allowWrite, loadBotSheet); } else { - BulletinFactory.of(lastFragment).createErrorBulletin(LocaleController.getString(R.string.BotAlreadyAddedToAttachMenu)).show(); - } - } else { - AttachBotIntroTopView introTopView = new AttachBotIntroTopView(LaunchActivity.this); - 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(LaunchActivity.this) - .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(intentAccount).getInputUser(peerId); - botRequest.enabled = true; - botRequest.write_allowed = allowWrite.get(); - - ConnectionsManager.getInstance(intentAccount).sendRequest(botRequest, (response2, error2) -> AndroidUtilities.runOnUIThread(() -> { - if (response2 instanceof TLRPC.TL_boolTrue) { - MediaDataController.getInstance(intentAccount).loadAttachMenuBots(false, true); - - if (dialogsActivity != null) { - presentFragment(dialogsActivity); - } else if (lastFragment instanceof ChatActivity) { - ((ChatActivity) lastFragment).openAttachBotLayout(user.id, setAsAttachBot); - } - } - }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); - }) - .setNegativeButton(LocaleController.getString(R.string.Cancel), null); - - if (attachMenuBot.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); + loadBotSheet.run(); } - builder.show(); - } - } else { - BulletinFactory.of(mainFragmentsStack.get(mainFragmentsStack.size() - 1)).createErrorBulletin(LocaleController.getString(R.string.BotCantAddToAttachMenu)).show(); + }); } - })); + }); + } + return; + } + } + + if (setAsAttachBot != null && attachMenuBotToOpen == null) { + TLRPC.User user = MessagesController.getInstance(intentAccount).getUser(peerId); + if (user != null && user.bot) { + if (user.bot_attach_menu) { + processAttachMenuBot(intentAccount, peerId, attachMenuBotChoose, user, setAsAttachBot); } else { BulletinFactory.of(mainFragmentsStack.get(mainFragmentsStack.size() - 1)).createErrorBulletin(LocaleController.getString(R.string.BotCantAddToAttachMenu)).show(); } @@ -4847,6 +4775,115 @@ public void onError() { } } + private void processAttachMenuBot(int intentAccount, long peerId, String attachMenuBotChoose, TLRPC.User user, String setAsAttachBot) { + TLRPC.TL_messages_getAttachMenuBot getAttachMenuBot = new TLRPC.TL_messages_getAttachMenuBot(); + getAttachMenuBot.bot = MessagesController.getInstance(intentAccount).getInputUser(peerId); + ConnectionsManager.getInstance(intentAccount).sendRequest(getAttachMenuBot, (response1, error1) -> AndroidUtilities.runOnUIThread(() -> { + if (response1 instanceof TLRPC.TL_attachMenuBotsBot) { + TLRPC.TL_attachMenuBotsBot attachMenuBotsBot = (TLRPC.TL_attachMenuBotsBot) response1; + MessagesController.getInstance(intentAccount).putUsers(attachMenuBotsBot.users, false); + TLRPC.TL_attachMenuBot attachMenuBot = attachMenuBotsBot.bot; + BaseFragment lastFragment_ = mainFragmentsStack.get(mainFragmentsStack.size() - 1); + if (AndroidUtilities.isTablet() && !(lastFragment_ instanceof ChatActivity) && !rightFragmentsStack.isEmpty()) { + lastFragment_ = rightFragmentsStack.get(rightFragmentsStack.size() - 1); + } + final BaseFragment lastFragment = lastFragment_; + + List chooserTargets = new ArrayList<>(); + if (!TextUtils.isEmpty(attachMenuBotChoose)) { + for (String target : attachMenuBotChoose.split(" ")) { + if (MediaDataController.canShowAttachMenuBotForTarget(attachMenuBot, target)) { + chooserTargets.add(target); + } + } + } + DialogsActivity dialogsActivity; + + if (!chooserTargets.isEmpty()) { + Bundle args = new Bundle(); + args.putInt("dialogsType", DialogsActivity.DIALOGS_TYPE_START_ATTACH_BOT); + 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")); + + dialogsActivity = new DialogsActivity(args); + dialogsActivity.setDelegate((fragment, dids, message1, param, topicsFragment) -> { + long did = dids.get(0).dialogId; + + Bundle args1 = new Bundle(); + args1.putBoolean("scrollToTopOnResume", true); + if (DialogObject.isEncryptedDialog(did)) { + args1.putInt("enc_id", DialogObject.getEncryptedChatId(did)); + } else if (DialogObject.isUserDialog(did)) { + args1.putLong("user_id", did); + } else { + args1.putLong("chat_id", -did); + } + args1.putString("attach_bot", UserObject.getPublicUsername(user)); + if (setAsAttachBot != null) { + args1.putString("attach_bot_start_command", setAsAttachBot); + } + if (MessagesController.getInstance(intentAccount).checkCanOpenChat(args1, fragment)) { + NotificationCenter.getInstance(intentAccount).postNotificationName(NotificationCenter.closeChats); + actionBarLayout.presentFragment(new ChatActivity(args1), true, false, true, false); + } + return true; + }); + } else { + dialogsActivity = null; + } + + if (!attachMenuBot.inactive) { + if (dialogsActivity != null) { + presentFragment(dialogsActivity); + } else if (lastFragment instanceof ChatActivity) { + ChatActivity chatActivity = (ChatActivity) lastFragment; + if (!MediaDataController.canShowAttachMenuBot(attachMenuBot, chatActivity.getCurrentUser() != null ? chatActivity.getCurrentUser() : chatActivity.getCurrentChat())) { + BulletinFactory.of(lastFragment).createErrorBulletin(LocaleController.getString(R.string.BotAlreadyAddedToAttachMenu)).show(); + return; + } + chatActivity.openAttachBotLayout(user.id, setAsAttachBot, false); + } else { + BulletinFactory.of(lastFragment).createErrorBulletin(LocaleController.getString(R.string.BotAlreadyAddedToAttachMenu)).show(); + } + } else { + AttachBotIntroTopView introTopView = new AttachBotIntroTopView(LaunchActivity.this); + introTopView.setColor(Theme.getColor(Theme.key_chat_attachIcon)); + introTopView.setBackgroundColor(Theme.getColor(Theme.key_dialogTopBackground)); + introTopView.setAttachBot(attachMenuBot); + + AtomicBoolean allowWrite = new AtomicBoolean(); + + WebAppDisclaimerAlert.show(this, (allowSendMessage) -> { + TLRPC.TL_messages_toggleBotInAttachMenu botRequest = new TLRPC.TL_messages_toggleBotInAttachMenu(); + botRequest.bot = MessagesController.getInstance(intentAccount).getInputUser(peerId); + botRequest.enabled = true; + botRequest.write_allowed = allowWrite.get(); + + ConnectionsManager.getInstance(intentAccount).sendRequest(botRequest, (response2, error2) -> AndroidUtilities.runOnUIThread(() -> { + if (response2 instanceof TLRPC.TL_boolTrue) { + MediaDataController.getInstance(intentAccount).loadAttachMenuBots(false, true, () -> { + if (dialogsActivity != null) { + presentFragment(dialogsActivity); + } else if (lastFragment instanceof ChatActivity) { + ((ChatActivity) lastFragment).openAttachBotLayout(user.id, setAsAttachBot, true); + } + }); + } + }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); + }, attachMenuBot.request_write_access ? user : null); + } + } else { + BulletinFactory.of(mainFragmentsStack.get(mainFragmentsStack.size() - 1)).createErrorBulletin(LocaleController.getString(R.string.BotCantAddToAttachMenu)).show(); + } + })); + } + private void openForumFromLink(long dialogId, int topicId, Integer messageId, Runnable onOpened) { if (messageId == null) { Bundle bundle = new Bundle(); @@ -5521,6 +5558,7 @@ private void onFinish() { if (currentAccount != -1) { NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.appDidLogout); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.mainUserInfoChanged); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.attachMenuBotsDidLoad); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.didUpdateConnectionState); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.needShowAlert); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.wasUnableToFindCurrentLocation); @@ -5760,6 +5798,7 @@ protected void onPause() { if (VoIPFragment.getInstance() != null) { VoIPFragment.onPause(); } + SpoilerEffect2.pause(true); } @Override @@ -5948,6 +5987,7 @@ protected void onResume() { VoIPFragment.onResume(); } invalidateTabletMode(); + SpoilerEffect2.pause(false); } private void invalidateTabletMode() { @@ -6052,6 +6092,8 @@ public void didReceivedNotification(int id, final int account, Object... args) { } } else if (id == NotificationCenter.mainUserInfoChanged) { drawerLayoutAdapter.notifyDataSetChanged(); + } else if (id == NotificationCenter.attachMenuBotsDidLoad) { + drawerLayoutAdapter.notifyDataSetChanged(); } else if (id == NotificationCenter.needShowAlert) { final Integer reason = (Integer) args[0]; if (reason == 6 || reason == 3 && proxyErrorDialog != null) { @@ -6554,7 +6596,7 @@ public void onAnimationEnd(Animator animation) { if (!mainFragmentsStack.isEmpty()) { BaseFragment fragment = mainFragmentsStack.get(mainFragmentsStack.size() - 1); if (fragment.getParentActivity() != null) { - fragment.showDialog(new LimitReachedBottomSheet(fragment, fragment.getParentActivity(), (int) args[0], currentAccount)); + fragment.showDialog(new LimitReachedBottomSheet(fragment, fragment.getParentActivity(), (int) args[0], currentAccount, null)); } } } else if (id == NotificationCenter.currentUserPremiumStatusChanged) { @@ -7582,6 +7624,18 @@ public void requestCustomNavigationBar() { } } + public int getNavigationBarColor() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + final Window window = getWindow(); + if (customNavigationBar != null) { + return drawerLayoutContainer.getNavigationBarColor(); + } else { + return window.getNavigationBarColor(); + } + } + return 0; + } + public void setNavigationBarColor(int color, boolean checkButtons) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { final Window window = getWindow(); @@ -7605,6 +7659,28 @@ public void setNavigationBarColor(int color, boolean checkButtons) { } } + private ValueAnimator navBarAnimator; + public void animateNavigationBarColor(int toColor) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + return; + } + if (navBarAnimator != null) { + navBarAnimator.cancel(); + navBarAnimator = null; + } + navBarAnimator = ValueAnimator.ofArgb(getNavigationBarColor(), toColor); + navBarAnimator.addUpdateListener(anm -> setNavigationBarColor((int) anm.getAnimatedValue(), false)); + navBarAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + setNavigationBarColor(toColor, false); + } + }); + navBarAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + navBarAnimator.setDuration(320); + navBarAnimator.start(); + } + public void setLightNavigationBar(boolean lightNavigationBar) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { final Window window = getWindow(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java index 56490a51c2..9255f8729b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java @@ -1622,7 +1622,7 @@ private void openProximityAlert() { } private void openShareLiveLocation(int proximityRadius) { - if (delegate == null || getParentActivity() == null || myLocation == null || !checkGpsEnabled()) { + if (delegate == null || disablePermissionCheck() || getParentActivity() == null || myLocation == null || !checkGpsEnabled()) { return; } if (checkBackgroundPermission && Build.VERSION.SDK_INT >= 29) { @@ -1945,6 +1945,9 @@ private void onMapInit() { } private boolean checkGpsEnabled() { + if (disablePermissionCheck()) { + return false; + } if (!getParentActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS)) { return true; } @@ -2744,7 +2747,9 @@ public void onResume() { } } fixLayoutInternal(true); - if (checkPermission && Build.VERSION.SDK_INT >= 23) { + if (disablePermissionCheck()) { + checkPermission = false; + } else if (checkPermission && Build.VERSION.SDK_INT >= 23) { Activity activity = getParentActivity(); if (activity != null) { checkPermission = false; @@ -2759,6 +2764,10 @@ public void onResume() { } } + protected boolean disablePermissionCheck() { + return false; + } + @Override public void onRequestPermissionsResultFragment(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == 30) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/MessageSeenView.java b/TMessagesProj/src/main/java/org/telegram/ui/MessageSeenView.java index 1b579e56c8..c7adda3a63 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/MessageSeenView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/MessageSeenView.java @@ -56,6 +56,7 @@ import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.MessageSeenCheckDrawable; import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.StatusBadgeComponent; import java.util.ArrayList; import java.util.HashMap; @@ -361,7 +362,7 @@ private static class UserCell extends FrameLayout implements NotificationCenter. SimpleTextView nameView; TextView readView; AvatarDrawable avatarDrawable = new AvatarDrawable(); - AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable rightDrawable; + StatusBadgeComponent statusBadgeComponent; TLObject object; @@ -379,7 +380,7 @@ public UserCell(Context context) { nameView.setTextColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem)); nameView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); - rightDrawable = new AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable(this, AndroidUtilities.dp(18)); + statusBadgeComponent = new StatusBadgeComponent(this); nameView.setDrawablePadding(AndroidUtilities.dp(3)); readView = new TextView(context); @@ -450,35 +451,20 @@ public void didReceivedNotification(int id, int account, Object... args) { } private void updateStatus(boolean animated) { - 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); - } + nameView.setRightDrawable(statusBadgeComponent.updateDrawable(object, Theme.getColor(Theme.key_chats_verifiedBackground), animated)); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - if (rightDrawable != null) { - rightDrawable.attach(); - } + statusBadgeComponent.onAttachedToWindow(); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.userEmojiStatusUpdated); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (rightDrawable != null) { - rightDrawable.detach(); - } + statusBadgeComponent.onDetachedFromWindow(); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.userEmojiStatusUpdated); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java index 0701194f9d..06e7edca4e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java @@ -8,6 +8,9 @@ package org.telegram.ui; +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.lerp; + import android.Manifest; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -30,6 +33,8 @@ 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; @@ -37,8 +42,10 @@ 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.SurfaceTexture; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; @@ -55,7 +62,6 @@ import android.text.Spannable; import android.text.SpannableString; import android.text.SpannableStringBuilder; -import android.text.Spanned; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; @@ -69,26 +75,27 @@ import android.transition.TransitionSet; import android.transition.TransitionValues; import android.util.FloatProperty; +import android.util.Log; import android.util.Pair; import android.util.Property; import android.util.Range; import android.util.SparseArray; import android.util.TypedValue; -import android.view.ActionMode; import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; -import android.view.Menu; import android.view.MotionEvent; import android.view.OrientationEventListener; import android.view.Surface; +import android.view.SurfaceView; import android.view.TextureView; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewOutlineProvider; +import android.view.ViewPropertyAnimator; import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.WindowManager; @@ -102,6 +109,7 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.OverScroller; +import android.widget.ScrollView; import android.widget.Scroller; import android.widget.TextView; import android.widget.Toast; @@ -114,8 +122,8 @@ import androidx.core.content.ContextCompat; import androidx.core.content.FileProvider; import androidx.core.graphics.ColorUtils; -import androidx.core.math.MathUtils; import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; import androidx.core.widget.NestedScrollView; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.FloatValueHolder; @@ -136,6 +144,7 @@ import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.Bitmaps; +import org.telegram.messenger.BotWebViewVibrationEffect; import org.telegram.messenger.BringAppForegroundService; import org.telegram.messenger.BuildVars; import org.telegram.messenger.ChatObject; @@ -185,7 +194,6 @@ import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.ActionBar.SimpleTextView; import org.telegram.ui.ActionBar.Theme; -import org.telegram.ui.Adapters.MentionsAdapter; import org.telegram.ui.Cells.CheckBoxCell; import org.telegram.ui.Cells.PhotoPickerPhotoCell; import org.telegram.ui.Cells.TextSelectionHelper; @@ -197,8 +205,10 @@ import org.telegram.ui.Components.AnimatedTextView; import org.telegram.ui.Components.AnimationProperties; import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.BlurringShader; import org.telegram.ui.Components.Bulletin; import org.telegram.ui.Components.BulletinFactory; +import org.telegram.ui.Components.CaptionPhotoViewer; import org.telegram.ui.Components.ChatAttachAlert; import org.telegram.ui.Components.CheckBox; import org.telegram.ui.Components.ClippingImageView; @@ -218,15 +228,12 @@ import org.telegram.ui.Components.LinkSpanDrawable; import org.telegram.ui.Components.LoadingDrawable; import org.telegram.ui.Components.MediaActivity; -import org.telegram.ui.Components.NumberPicker; import org.telegram.ui.Components.OptionsSpeedIconDrawable; import org.telegram.ui.Components.OtherDocumentPlaceholderDrawable; import org.telegram.ui.Components.Paint.Views.LPhotoPaintView; import org.telegram.ui.Components.PaintingOverlay; import org.telegram.ui.Components.PhotoCropView; import org.telegram.ui.Components.PhotoFilterView; -import org.telegram.ui.Components.PhotoPaintView; -import org.telegram.ui.Components.PhotoViewerCaptionEnterView; import org.telegram.ui.Components.PhotoViewerWebView; import org.telegram.ui.Components.PickerBottomLayoutViewer; import org.telegram.ui.Components.PipVideoOverlay; @@ -235,6 +242,7 @@ import org.telegram.ui.Components.RLottieDrawable; import org.telegram.ui.Components.RadialProgressView; import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.ScaleStateListAnimator; import org.telegram.ui.Components.ShareAlert; import org.telegram.ui.Components.SizeNotifierFrameLayoutPhoto; import org.telegram.ui.Components.SpeedIconDrawable; @@ -243,8 +251,8 @@ import org.telegram.ui.Components.Tooltip; import org.telegram.ui.Components.TranslateAlert2; import org.telegram.ui.Components.URLSpanReplacement; -import org.telegram.ui.Components.URLSpanUserMentionPhotoViewer; import org.telegram.ui.Components.UndoView; +import org.telegram.ui.Components.VideoCompressButton; import org.telegram.ui.Components.VideoEditTextureView; import org.telegram.ui.Components.VideoForwardDrawable; import org.telegram.ui.Components.VideoPlayer; @@ -254,6 +262,7 @@ import org.telegram.ui.Components.ViewHelper; import org.telegram.ui.Components.spoilers.SpoilersTextView; import org.telegram.ui.Stories.DarkThemeResourceProvider; +import org.telegram.ui.Stories.recorder.KeyboardNotifier; import java.io.ByteArrayInputStream; import java.io.File; @@ -290,6 +299,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private final static float ZOOM_SCALE = 0.1f; private final static int MARK_DEFERRED_IMAGE_LOADING = 1; + private boolean ALLOW_USE_SURFACE = Build.VERSION.SDK_INT >= 30; + private int classGuid; private PhotoViewerProvider placeProvider; private boolean isVisible; @@ -319,6 +330,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private PhotoCountView countView; public boolean closePhotoAfterSelect = true; private TextSelectionHelper.SimpleTextSelectionHelper textSelectionHelper; + private boolean firstFrameRendered; + private Paint surfaceBlackoutPaint; public TextureView getVideoTextureView() { return videoTextureView; @@ -328,6 +341,10 @@ public boolean isVisibleOrAnimating() { return isVisibleOrAnimating; } + public SurfaceView getVideoSurfaceView() { + return videoSurfaceView; + } + private static class PhotoViewerActionBarContainer extends FrameLayout implements NotificationCenter.NotificationCenterDelegate { private FrameLayout container; @@ -339,12 +356,18 @@ public PhotoViewerActionBarContainer(Context context) { super(context); container = new FrameLayout(context); - container.setPadding(AndroidUtilities.dp((AndroidUtilities.isTablet() ? 80 : 72) - 16), 0, 0, 0); + container.setPadding(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 = new FrameLayout(context) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + setPivotY(getMeasuredHeight()); + } + }; + titleLayout.setPivotX(dp(16)); + titleLayout.setPadding(dp(16), 0, 0, 0); titleLayout.setClipToPadding(false); container.addView(titleLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); @@ -355,20 +378,26 @@ public PhotoViewerActionBarContainer(Context context) { titleTextView[i].setTextColor(0xffffffff); titleTextView[i].setTextSize(20); titleTextView[i].setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - titleTextView[i].setDrawablePadding(AndroidUtilities.dp(4)); + titleTextView[i].setDrawablePadding(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.setTextSize(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 setTextShadows(boolean applyShadows) { + titleTextView[0].getPaint().setShadowLayer(AndroidUtilities.dpf2(0.66f), 0, 1, applyShadows ? 0x72000000 : 0); + titleTextView[1].getPaint().setShadowLayer(AndroidUtilities.dpf2(0.66f), 0, 1, applyShadows ? 0x72000000 : 0); + subtitleTextView.getDrawable().setShadowLayer(AndroidUtilities.dpf2(0.66f), 0, 1, applyShadows ? 0x72000000 : 0); + } + public void setTitle(CharSequence title) { titleTextView[1].setAlpha(0); titleTextView[1].setVisibility(View.GONE); @@ -417,7 +446,7 @@ public void setTitleAnimated(CharSequence title, boolean axis, boolean direction titleTextView[0].resetScrolling(); titleTextView[0].setText(title); - float amplitude = AndroidUtilities.dp(8) * (direction ? 1 : -1); + float amplitude = dp(8) * (direction ? 1 : -1); titleTextView[1].setTranslationX(0); titleTextView[1].setTranslationY(0); @@ -470,15 +499,15 @@ public void setSubtitle(CharSequence subtitle, boolean animated) { } final boolean isLandscape = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y; - final int subtitleTranslation = AndroidUtilities.dp((haveSubtitle ? 30 : 33) - (isLandscape ? 6 : 0)); + final int subtitleTranslation = 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)); + arrayList.add(ObjectAnimator.ofFloat(titleLayout, View.TRANSLATION_Y, haveSubtitle ? dp(-12) : 0)); + arrayList.add(ObjectAnimator.ofFloat(titleLayout, View.SCALE_X, haveSubtitle ? .87f : 1)); + arrayList.add(ObjectAnimator.ofFloat(titleLayout, View.SCALE_Y, haveSubtitle ? .87f : 1)); subtitleAnimator = new AnimatorSet(); subtitleAnimator.playTogether(arrayList); subtitleAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); @@ -486,9 +515,9 @@ public void setSubtitle(CharSequence subtitle, boolean animated) { } 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); + titleLayout.setTranslationY(haveSubtitle ? dp(-12) : 0); + titleLayout.setScaleX(haveSubtitle ? .87f : 1); + titleLayout.setScaleY(haveSubtitle ? .87f : 1); } } subtitleTextView.setText(subtitle, animated); @@ -595,21 +624,21 @@ public PhotoCountView(Context context) { 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.setTextSize(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.setTextSize(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.setTextSize(dp(14)); right.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); right.setCallback(this); right.setText("0"); @@ -617,7 +646,7 @@ public PhotoCountView(Context context) { } private void setCenterText() { - center = new StaticLayout(getOf(), paint, AndroidUtilities.dp(200), Layout.Alignment.ALIGN_CENTER, 1, 0, false); + center = new StaticLayout(getOf(), paint, dp(200), Layout.Alignment.ALIGN_CENTER, 1, 0, false); if (center.getLineCount() >= 1) { centerWidth = center.getLineWidth(0); centerTop = center.getLineDescent(0); @@ -691,8 +720,8 @@ protected void onDraw(Canvas canvas) { return; } - float width = left.getCurrentWidth() + centerWidth + right.getCurrentWidth() + AndroidUtilities.dp(9 + 9); - float marginTop = this.marginTop + (1f - show) * -AndroidUtilities.dp(8); + float width = left.getCurrentWidth() + centerWidth + right.getCurrentWidth() + dp(9 + 9); + float marginTop = this.marginTop + (1f - show) * -dp(8); AndroidUtilities.rectTmp.set( (getWidth() - width) / 2f, @@ -711,20 +740,20 @@ protected void onDraw(Canvas canvas) { 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)); + canvas.translate((getWidth() - width) / 2f + dp(9), marginTop + dp(10)); + left.setBounds(0, 0, (int) left.getCurrentWidth(), 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); + canvas.translate(-(center.getWidth() - centerWidth) / 2f, (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.setBounds(0, 0, (int) right.getCurrentWidth(), dp(23)); right.setAlpha((int) (0xFF * show)); right.draw(canvas); canvas.restore(); @@ -738,7 +767,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { right.setOverrideFullWidth(width); super.onMeasure( MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(marginTop + AndroidUtilities.dp(10 + 23 + 10), MeasureSpec.EXACTLY) + MeasureSpec.makeMeasureSpec(marginTop + dp(10 + 23 + 10), MeasureSpec.EXACTLY) ); } } @@ -761,6 +790,8 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { private int videoWidth, videoHeight; private float inlineOutAnimationProgress; + private BlurringShader.BlurManager blurManager; + private BlurringShader.StoryBlurDrawer shadowBlurer; private WindowManager.LayoutParams windowLayoutParams; private FrameLayoutDrawer containerView; private PhotoViewerWebView photoViewerWebView; @@ -800,11 +831,9 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { private ImageView cropItem; private ImageView mirrorItem; private ImageView rotateItem; - private ImageView cameraItem; private ImageView tuneItem; - private ImageView timeItem; private ImageView muteItem; - private ImageView compressItem; + private VideoCompressButton compressItem; private GroupedPhotosListView groupedPhotosListView; private Tooltip tooltip; private UndoView hintView; @@ -825,8 +854,6 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { private float[] pressedDrawableAlpha = new float[2]; private int touchSlop; - private boolean useSmoothKeyboard; - private VideoForwardDrawable videoForwardDrawable; private AnimatorSet currentListViewAnimation; @@ -837,13 +864,18 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { private MediaController.CropState leftCropState; private MediaController.CropState rightCropState; private PhotoFilterView photoFilterView; + private AnimatorSet paintKeyboardAnimator; + private KeyboardNotifier paintKeyboardNotifier; private LPhotoPaintView photoPaintView; private AlertDialog visibleDialog; private CaptionTextViewSwitcher captionTextViewSwitcher; private CaptionScrollView captionScrollView; + private CaptionPhotoViewer captionEdit; + private float shiftDp = -8; + private FrameLayout captionEditContainer; private FrameLayout captionContainer; private ChatAttachAlert parentAlert; - private PhotoViewerCaptionEnterView captionEditText; +// private PhotoViewerCaptionEnterView captionEditText; private int sendPhotoType; private boolean cropInitied; private boolean isDocumentsPicker; @@ -860,7 +892,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { private boolean pipAvailable; - private Object lastInsets; + private final Rect insets = new Rect(); private boolean padImageForHorizontalInsets; private boolean doneButtonPressed; @@ -904,6 +936,8 @@ public void run() { private View flashView; private AnimatorSet flashAnimator; private TextureView videoTextureView; + private SurfaceView videoSurfaceView; + private boolean usedSurfaceView; private FirstFrameView firstFrameView; private VideoPlayer videoPlayer; private boolean manuallyPaused; @@ -1035,7 +1069,6 @@ public Float get(View object) { } }; - private TextView captionLimitView; private Drawable pickerViewSendDrawable; private CharSequence customTitle; public boolean skipLastFrameDraw; @@ -1317,7 +1350,7 @@ public void run() { if (shownControlsByEnd && !actionBarWasShownBeforeByEnd) { progress = 0; } - if (!inPreview && (currentEditMode != EDIT_MODE_NONE || videoTimelineView.getVisibility() == View.VISIBLE)) { + if (!inPreview && (currentEditMode != EDIT_MODE_NONE || videoTimelineViewContainer.getVisibility() == View.VISIBLE)) { if (progress >= videoTimelineView.getRightProgress()) { videoTimelineView.setProgress(videoTimelineView.getLeftProgress()); videoPlayer.seekTo((int) (videoTimelineView.getLeftProgress() * getVideoDuration())); @@ -1358,7 +1391,7 @@ public void run() { bufferedProgress = -1; } } - if (!inPreview && videoTimelineView.getVisibility() == View.VISIBLE) { + if (!inPreview && videoTimelineViewContainer.getVisibility() == View.VISIBLE) { if (progress >= videoTimelineView.getRightProgress()) { manuallyPaused = false; pauseVideoOrWeb(); @@ -1439,38 +1472,66 @@ public void run() { } changingTextureView = true; - if (textureImageView != null) { - try { + + TextureViewContainer textureViewContainer = new TextureViewContainer(parentActivity); + + try { + if (usedSurfaceView) { + Drawable drawable = textureImageView.getDrawable(); + if (drawable instanceof BitmapDrawable) { + currentBitmap = ((BitmapDrawable) drawable).getBitmap(); + } else { + currentBitmap = Bitmaps.createBitmap(videoSurfaceView.getWidth(), videoSurfaceView.getHeight(), Bitmap.Config.ARGB_8888); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { + AndroidUtilities.getBitmapFromSurface(videoSurfaceView, currentBitmap); + } + } + } else { currentBitmap = Bitmaps.createBitmap(videoTextureView.getWidth(), videoTextureView.getHeight(), Bitmap.Config.ARGB_8888); videoTextureView.getBitmap(currentBitmap); - } catch (Throwable e) { - if (currentBitmap != null) { - currentBitmap.recycle(); - currentBitmap = null; - } - FileLog.e(e); } - + } catch (Throwable e) { if (currentBitmap != null) { + currentBitmap.recycle(); + currentBitmap = null; + } + FileLog.e(e); + } + + if (currentBitmap != null) { + if (textureImageView != null) { textureImageView.setVisibility(View.VISIBLE); textureImageView.setImageBitmap(currentBitmap); - } else { - textureImageView.setImageDrawable(null); } + textureViewContainer.imageReceiver.setImageBitmap(currentBitmap); } isInline = true; - - changedTextureView = new TextureView(parentActivity); - if (PipVideoOverlay.show(false, parentActivity, changedTextureView, videoWidth, videoHeight, pipVideoOverlayAnimateFlag)) { + changedTextureView = textureViewContainer.textureView; + if (PipVideoOverlay.show(false, parentActivity, textureViewContainer, videoWidth, videoHeight, pipVideoOverlayAnimateFlag)) { PipVideoOverlay.setPhotoViewer(PhotoViewer.this); } pipVideoOverlayAnimateFlag = true; - changedTextureView.setVisibility(View.INVISIBLE); - if (aspectRatioFrameLayout != null) { - aspectRatioFrameLayout.removeView(videoTextureView); + if (usedSurfaceView) { + if (aspectRatioFrameLayout != null) { + aspectRatioFrameLayout.removeView(videoTextureView); + aspectRatioFrameLayout.removeView(videoSurfaceView); + } + videoPlayer.setSurfaceView(null); + videoPlayer.setTextureView(null); + videoPlayer.play(); + videoPlayer.setTextureView(changedTextureView); + checkChangedTextureView(true); + changedTextureView.setVisibility(View.VISIBLE); + } else { + changedTextureView.setVisibility(View.INVISIBLE); + if (aspectRatioFrameLayout != null) { + aspectRatioFrameLayout.removeView(videoTextureView); + aspectRatioFrameLayout.removeView(videoSurfaceView); + } } + } }; @@ -1506,53 +1567,173 @@ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { if (waitingForFirstTextureUpload == 1) { - changedTextureView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - changedTextureView.getViewTreeObserver().removeOnPreDrawListener(this); - if (textureImageView != null) { - if (isInline) { - AndroidUtilities.runOnUIThread(()-> { - textureImageView.setVisibility(View.INVISIBLE); - textureImageView.setImageDrawable(null); - if (currentBitmap != null) { - currentBitmap.recycle(); - currentBitmap = null; - } - }, 300); - } else { + checkChangedTextureView(true); + } + } + }; + + private void checkChangedTextureView(boolean enter) { + if (enter) { + if (changedTextureView == null) { + return; + } + changedTextureView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + changedTextureView.getViewTreeObserver().removeOnPreDrawListener(this); + if (textureImageView != null) { + if (isInline) { + AndroidUtilities.runOnUIThread(() -> { textureImageView.setVisibility(View.INVISIBLE); textureImageView.setImageDrawable(null); if (currentBitmap != null) { currentBitmap.recycle(); currentBitmap = null; } + }, 300); + } else { + textureImageView.setVisibility(View.INVISIBLE); + textureImageView.setImageDrawable(null); + if (currentBitmap != null) { + currentBitmap.recycle(); + currentBitmap = null; } } - AndroidUtilities.runOnUIThread(() -> { - if (isInline) { - dismissInternal(); - } - }); - waitingForFirstTextureUpload = 0; - return true; } - }); - changedTextureView.invalidate(); + AndroidUtilities.runOnUIThread(() -> { + if (isInline) { + dismissInternal(); + } + }); + waitingForFirstTextureUpload = 0; + return true; + } + }); + changedTextureView.invalidate(); + } else { + if (waitingForFirstTextureUpload == 2) { + if (textureImageView != null) { +// if (usedSurfaceView) { +// if (currentBitmap != null) { +// currentBitmap.recycle(); +// currentBitmap = null; +// } +// textureImageView.setVisibility(View.VISIBLE); +// textureImageView.setImageBitmap(currentBitmap = changedTextureView.getBitmap()); +// } else { + textureImageView.setVisibility(View.INVISIBLE); + textureImageView.setImageDrawable(null); + if (currentBitmap != null) { + currentBitmap.recycle(); + currentBitmap = null; + } + // } + + } + switchingInlineMode = false; + + if (Build.VERSION.SDK_INT >= 21) { + View textureView = usedSurfaceView ? videoSurfaceView : videoTextureView; + if (aspectRatioFrameLayout == null) { + return; + } + aspectRatioFrameLayout.getLocationInWindow(pipPosition); + //pipPosition[0] -= getLeftInset(); + pipPosition[1] -= containerView.getTranslationY(); + if (textureImageView != null) { + textureImageView.setTranslationX(textureImageView.getTranslationX() + getLeftInset()); + } + if (textureView != null) { + textureView.setTranslationX(textureView.getTranslationX() + getLeftInset() - aspectRatioFrameLayout.getX()); + } + if (firstFrameView != null) { + firstFrameView.setTranslationX(textureView.getTranslationX()); + } + + ValueAnimator progressAnimator = ValueAnimator.ofFloat(0, 1); + progressAnimator.addUpdateListener(animation -> clippingImageProgress = 1f - (float) animation.getAnimatedValue()); + + float toX = usedSurfaceView ? 0 : pipPosition[0] - aspectRatioFrameLayout.getX(); + float toY = usedSurfaceView ? 0 : pipPosition[1] - aspectRatioFrameLayout.getY(); + AnimatorSet animatorSet = new AnimatorSet(); + ArrayList animators = new ArrayList<>(); + animators.add(progressAnimator); + animators.add(ObjectAnimator.ofFloat(textureImageView, View.SCALE_X, 1.0f)); + animators.add(ObjectAnimator.ofFloat(textureImageView, View.SCALE_Y, 1.0f)); + animators.add(ObjectAnimator.ofFloat(textureImageView, View.TRANSLATION_X, usedSurfaceView ? 0 : pipPosition[0])); + animators.add(ObjectAnimator.ofFloat(textureImageView, View.TRANSLATION_Y, usedSurfaceView ? 0 : pipPosition[1])); + animators.add(ObjectAnimator.ofFloat(textureView, View.SCALE_X, 1.0f)); + animators.add(ObjectAnimator.ofFloat(textureView, View.SCALE_Y, 1.0f)); + animators.add(ObjectAnimator.ofFloat(textureView, View.TRANSLATION_X, toX)); + animators.add(ObjectAnimator.ofFloat(textureView, View.TRANSLATION_Y, toY)); + animators.add(ObjectAnimator.ofInt(backgroundDrawable, AnimationProperties.COLOR_DRAWABLE_ALPHA, 255)); + if (firstFrameView != null) { + animators.add(ObjectAnimator.ofFloat(firstFrameView, View.SCALE_X, 1.0f)); + animators.add(ObjectAnimator.ofFloat(firstFrameView, View.SCALE_Y, 1.0f)); + animators.add(ObjectAnimator.ofFloat(firstFrameView, View.TRANSLATION_X, toX)); + animators.add(ObjectAnimator.ofFloat(firstFrameView, View.TRANSLATION_Y, toY)); + } + + org.telegram.ui.Components.Rect pipRect = PipVideoOverlay.getPipRect(false, aspectRatioFrameLayout.getAspectRatio()); + float scale = pipRect.width / textureView.getWidth(); + ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1); + valueAnimator.addUpdateListener(animation -> { + inlineOutAnimationProgress = (float) animation.getAnimatedValue(); + textureView.invalidateOutline(); + if (textureImageView != null) { + textureImageView.invalidateOutline(); + } + if (firstFrameView != null) { + firstFrameView.invalidateOutline(); + } + }); + animators.add(valueAnimator); + + animatorSet.playTogether(animators); + final DecelerateInterpolator interpolator = new DecelerateInterpolator(); + animatorSet.setInterpolator(interpolator); + animatorSet.setDuration(250); + if (videoSurfaceView != null) { + videoSurfaceView.setVisibility(View.VISIBLE); + } + animatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + pipAnimationInProgress = false; + textureView.setOutlineProvider(null); + if (textureImageView != null) { + textureImageView.setOutlineProvider(null); + } + if (firstFrameView != null) { + firstFrameView.setOutlineProvider(null); + } + if (videoSurfaceView != null) { + videoSurfaceView.setVisibility(View.VISIBLE); + } + } + }); + animatorSet.start(); + toggleActionBar(true, true, new ActionBarToggleParams().enableStatusBarAnimation(false).enableTranslationAnimation(false).animationDuration(250).animationInterpolator(interpolator)); + } else { + toggleActionBar(true, false); + //containerView.setTranslationY(0); + } + + waitingForFirstTextureUpload = 0; } } - }; + } private float[][] animationValues = new float[2][13]; private ChatActivity parentChatActivity; private BaseFragment parentFragment; - private MentionsAdapter mentionsAdapter; - private RecyclerListView mentionListView; - private LinearLayoutManager mentionLayoutManager; - private SpringAnimation mentionListAnimation; - private boolean mentionListViewVisible; - private boolean allowMentions; +// private MentionsAdapter mentionsAdapter; +// private RecyclerListView mentionListView; +// private LinearLayoutManager mentionLayoutManager; +// private SpringAnimation mentionListAnimation; +// private boolean mentionListViewVisible; +// private boolean allowMentions; private ActionBarPopupWindow sendPopupWindow; private ActionBarPopupWindow.ActionBarPopupWindowLayout sendPopupLayout; @@ -1621,7 +1802,7 @@ public void restore() { } else { windowLayoutParams.flags = WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; } - windowLayoutParams.softInputMode = (useSmoothKeyboard ? WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN : WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) | WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; + windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; windowView.setFocusable(false); containerView.setFocusable(false); backgroundDrawable.setAlpha(255); @@ -1639,6 +1820,9 @@ public void restore() { private ImageReceiver leftImage = new ImageReceiver(); private ImageReceiver centerImage = new ImageReceiver(); private ImageReceiver rightImage = new ImageReceiver(); + private BlurringShader.ThumbBlurer leftBlur = new BlurringShader.ThumbBlurer(1, this::invalidateBlur); + private BlurringShader.ThumbBlurer centerBlur = new BlurringShader.ThumbBlurer(1, this::invalidateBlur); + private BlurringShader.ThumbBlurer rightBlur = new BlurringShader.ThumbBlurer(1, this::invalidateBlur); private boolean leftImageIsVideo; private boolean centerImageIsVideo; private boolean rightImageIsVideo; @@ -1646,6 +1830,8 @@ public void restore() { private Bitmap videoFrameBitmap = null; private int currentIndex; private int switchingToIndex; + private boolean editing; + private boolean fancyShadows; private MessageObject currentMessageObject; private Uri currentPlayingVideoFile; private EditState editState = new EditState(); @@ -1693,6 +1879,7 @@ public void restore() { private float dragY; private float translationX; private float translationY; + private float translateY; private float scale = 1; private float rotate = 0; private float mirror = 0; @@ -1741,17 +1928,17 @@ public void restore() { private boolean bottomTouchEnabled = true; - private ArrayList imagesArrTemp = new ArrayList<>(); - private SparseArray[] imagesByIdsTemp = new SparseArray[]{new SparseArray<>(), new SparseArray<>()}; - private ArrayList imagesArr = new ArrayList<>(); - private SparseArray[] imagesByIds = new SparseArray[]{new SparseArray<>(), new SparseArray<>()}; - private ArrayList imagesArrLocations = new ArrayList<>(); - private ArrayList imagesArrLocationsVideo = new ArrayList<>(); - private ArrayList imagesArrLocationsSizes = new ArrayList<>(); - private ArrayList imagesArrMessages = new ArrayList<>(); - private ArrayList secureDocuments = new ArrayList<>(); - private ArrayList avatarsArr = new ArrayList<>(); - private ArrayList imagesArrLocals = new ArrayList<>(); + private final ArrayList imagesArrTemp = new ArrayList<>(); + private final SparseArray[] imagesByIdsTemp = new SparseArray[] {new SparseArray<>(), new SparseArray<>()}; + private final ArrayList imagesArr = new ArrayList<>(); + private final SparseArray[] imagesByIds = new SparseArray[] {new SparseArray<>(), new SparseArray<>()}; + private final ArrayList imagesArrLocations = new ArrayList<>(); + private final ArrayList imagesArrLocationsVideo = new ArrayList<>(); + private final ArrayList imagesArrLocationsSizes = new ArrayList<>(); + private final ArrayList imagesArrMessages = new ArrayList<>(); + private final ArrayList secureDocuments = new ArrayList<>(); + private final ArrayList avatarsArr = new ArrayList<>(); + private final ArrayList imagesArrLocals = new ArrayList<>(); private ImageLocation currentAvatarLocation = null; private SavedState savedState = null; @@ -1863,7 +2050,7 @@ public void draw(Canvas canvas) { if (animationInProgress != 0 && !AndroidUtilities.isTablet() && currentPlaceObject != null && currentPlaceObject.animatingImageView != null) { animatingImageView.getClippedVisibleRect(visibleRect); if (!visibleRect.isEmpty()) { - visibleRect.inset(AndroidUtilities.dp(1f), AndroidUtilities.dp(1f)); + visibleRect.inset(dp(1f), dp(1f)); final Rect boundsRect = getBounds(); final float width = boundsRect.right; @@ -1910,7 +2097,7 @@ public SelectedPhotosListView(Context context) { setWillNotDraw(false); setClipToPadding(false); - setTranslationY(-AndroidUtilities.dp(10)); + setTranslationY(-dp(10)); DefaultItemAnimator defaultItemAnimator; setItemAnimator(defaultItemAnimator = new DefaultItemAnimator() { @Override @@ -1920,7 +2107,7 @@ protected void onMoveAnimationUpdate(ViewHolder holder) { }); defaultItemAnimator.setDelayAnimations(false); defaultItemAnimator.setSupportsChangeAnimations(false); - setPadding(AndroidUtilities.dp(12), AndroidUtilities.dp(12), AndroidUtilities.dp(12), AndroidUtilities.dp(6)); + setPadding(dp(12), dp(12), dp(12), dp(6)); paint.setColor(0x7f000000); arrowDrawable = context.getResources().getDrawable(R.drawable.photo_tooltip2).mutate(); @@ -1932,8 +2119,8 @@ public void onDraw(Canvas c) { int count = getChildCount(); if (count > 0) { - int x = getMeasuredWidth() - AndroidUtilities.dp(87); - arrowDrawable.setBounds(x, 0, x + arrowDrawable.getIntrinsicWidth(), AndroidUtilities.dp(6)); + int x = getMeasuredWidth() - dp(87); + arrowDrawable.setBounds(x, 0, x + arrowDrawable.getIntrinsicWidth(), dp(6)); arrowDrawable.draw(c); int minX = Integer.MAX_VALUE; @@ -1944,8 +2131,8 @@ public void onDraw(Canvas c) { maxX = (int) Math.max(maxX, Math.ceil(v.getX() + v.getMeasuredWidth())); } if (minX != Integer.MAX_VALUE && maxX != Integer.MIN_VALUE) { - rect.set(minX - AndroidUtilities.dp(6), AndroidUtilities.dp(6), maxX + AndroidUtilities.dp(6), AndroidUtilities.dp(6 + 85 + 12)); - c.drawRoundRect(rect, AndroidUtilities.dp(8), AndroidUtilities.dp(8), paint); + rect.set(minX - dp(6), dp(6), maxX + dp(6), dp(6 + 85 + 12)); + c.drawRoundRect(rect, dp(8), dp(8), paint); } } } @@ -1965,13 +2152,13 @@ private static class CounterView extends View { public CounterView(Context context) { super(context); textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); - textPaint.setTextSize(AndroidUtilities.dp(15)); + textPaint.setTextSize(dp(15)); textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); textPaint.setColor(0xffffffff); paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(0xffffffff); - paint.setStrokeWidth(AndroidUtilities.dp(2)); + paint.setStrokeWidth(dp(2)); paint.setStyle(Paint.Style.STROKE); paint.setStrokeJoin(Paint.Join.ROUND); @@ -2000,7 +2187,7 @@ public float getRotationX() { } public void setCount(int value) { - staticLayout = new StaticLayout("" + Math.max(1, value), textPaint, AndroidUtilities.dp(100), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + staticLayout = new StaticLayout("" + Math.max(1, value), textPaint, dp(100), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); width = (int) Math.ceil(staticLayout.getLineWidth(0)); height = staticLayout.getLineBottom(0); AnimatorSet animatorSet = new AnimatorSet(); @@ -2038,27 +2225,27 @@ public void setCount(int value) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(MeasureSpec.makeMeasureSpec(Math.max(width + AndroidUtilities.dp(20), AndroidUtilities.dp(30)), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(40), MeasureSpec.EXACTLY)); + super.onMeasure(MeasureSpec.makeMeasureSpec(Math.max(width + dp(20), dp(30)), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(dp(40), MeasureSpec.EXACTLY)); } @Override protected void onDraw(Canvas canvas) { int cy = getMeasuredHeight() / 2; paint.setAlpha(255); - rect.set(AndroidUtilities.dp(1), cy - AndroidUtilities.dp(14), getMeasuredWidth() - AndroidUtilities.dp(1), cy + AndroidUtilities.dp(14)); - canvas.drawRoundRect(rect, AndroidUtilities.dp(15), AndroidUtilities.dp(15), paint); + rect.set(dp(1), cy - dp(14), getMeasuredWidth() - dp(1), cy + dp(14)); + canvas.drawRoundRect(rect, dp(15), dp(15), paint); if (staticLayout != null) { textPaint.setAlpha((int) ((1.0f - rotation) * 255)); canvas.save(); - canvas.translate((getMeasuredWidth() - width) / 2, (getMeasuredHeight() - height) / 2 + AndroidUtilities.dpf2(0.2f) + rotation * AndroidUtilities.dp(5)); + canvas.translate((getMeasuredWidth() - width) / 2, (getMeasuredHeight() - height) / 2 + AndroidUtilities.dpf2(0.2f) + rotation * dp(5)); staticLayout.draw(canvas); canvas.restore(); paint.setAlpha((int) (rotation * 255)); int cx = (int) rect.centerX(); cy = (int) rect.centerY(); - cy -= AndroidUtilities.dp(5) * (1.0f - rotation); - canvas.drawLine(cx + AndroidUtilities.dp(5), cy - AndroidUtilities.dp(5), cx - AndroidUtilities.dp(5), cy + AndroidUtilities.dp(5), paint); - canvas.drawLine(cx - AndroidUtilities.dp(5), cy - AndroidUtilities.dp(5), cx + AndroidUtilities.dp(5), cy + AndroidUtilities.dp(5), paint); + cy -= dp(5) * (1.0f - rotation); + canvas.drawLine(cx + dp(5), cy - dp(5), cx - dp(5), cy + dp(5), paint); + canvas.drawLine(cx - dp(5), cy - dp(5), cx + dp(5), cy + dp(5), paint); } } } @@ -2074,7 +2261,7 @@ private class PhotoProgressView { private RectF progressRect = new RectF(); private int backgroundState = -1; private View parent; - private int size = AndroidUtilities.dp(64); + private int size = dp(64); private int previousBackgroundState = -2; private float animatedAlphaValue = 1.0f; private float[] animAlphas = new float[3]; @@ -2091,7 +2278,7 @@ public PhotoProgressView(View parentView) { progressPaint = new Paint(Paint.ANTI_ALIAS_FLAG); progressPaint.setStyle(Paint.Style.STROKE); progressPaint.setStrokeCap(Paint.Cap.ROUND); - progressPaint.setStrokeWidth(AndroidUtilities.dp(3)); + progressPaint.setStrokeWidth(dp(3)); progressPaint.setColor(0xffffffff); } parent = parentView; @@ -2263,7 +2450,7 @@ public int getY() { int y = ((AndroidUtilities.displaySize.y + (isStatusBarVisible() ? AndroidUtilities.statusBarHeight : 0)) - (int) (size * scale)) / 2; y += currentPanTranslationY; if (sendPhotoType == SELECT_TYPE_AVATAR) { - y -= AndroidUtilities.dp(38); + y -= dp(38); } return y; } @@ -2308,7 +2495,7 @@ public void onDraw(Canvas canvas) { } if (backgroundState == PROGRESS_EMPTY || backgroundState == PROGRESS_CANCEL || previousBackgroundState == PROGRESS_EMPTY || previousBackgroundState == PROGRESS_CANCEL) { - int diff = AndroidUtilities.dp(4); + int diff = dp(4); if (previousBackgroundState != -2) { progressPaint.setAlpha((int) (255 * animatedAlphaValue * alpha)); } else { @@ -2617,127 +2804,6 @@ private class FrameLayoutDrawer extends SizeNotifierFrameLayoutPhoto { private boolean ignoreLayout; private boolean captionAbove; - AdjustPanLayoutHelper adjustPanLayoutHelper = new AdjustPanLayoutHelper(this, false) { - @Override - protected void onPanTranslationUpdate(float y, float progress, boolean keyboardVisible) { - currentPanTranslationY = y; - if (currentEditMode != EDIT_MODE_PAINT) { - actionBar.setTranslationY(y); - if (countView != null) { - countView.setTranslationY(y); - } - } - if (miniProgressView != null) { - miniProgressView.setTranslationY(y); - } - if (progressView != null) { - progressView.setTranslationY(y); - } - if (checkImageView != null) { - checkImageView.setTranslationY(y); - } - if (photosCounterView != null) { - photosCounterView.setTranslationY(y); - } - if (selectedPhotosListView != null) { - selectedPhotosListView.setTranslationY(y); - } - if (aspectRatioFrameLayout != null) { - aspectRatioFrameLayout.setTranslationY(y); - } - if (textureImageView != null) { - textureImageView.setTranslationY(y); - } - if (photoCropView != null) { - photoCropView.setTranslationY(y); - } - if (photoFilterView != null) { - photoFilterView.setTranslationY(y); - } -// - if (pickerView != null) { - pickerView.setTranslationY(y); - } - if (pickerViewSendButton != null) { - pickerViewSendButton.setTranslationY(y); - } - - if (currentEditMode == EDIT_MODE_PAINT) { - if (captionEditText != null) { - captionEditText.setTranslationY(y); - } - - if (photoPaintView != null) { -// photoPaintView.getView().setTranslationY(y); -// photoPaintView.setOffsetTranslationY(y, progress, getKeyboardHeight(), true); - } - } else { - - if (photoPaintView != null) { - photoPaintView.getView().setTranslationY(y); - } - if (captionEditText != null) { - float p = progress < 0.5f ? 0 : (progress - 0.5f) / 0.5f; - captionEditText.setAlpha(p); - captionEditText.setTranslationY(y - this.keyboardSize + AndroidUtilities.dp(keyboardSize / 2) * (1f - progress)); - } - } - if (muteItem != null) { - muteItem.setTranslationY(y); - } - if (cameraItem != null) { - cameraItem.setTranslationY(y); - } - if (captionLimitView != null) { - captionLimitView.setTranslationY(y); - } - invalidate(); - } - - @Override - protected void onTransitionStart(boolean keyboardVisible, int contentHeight) { - navigationBar.setVisibility(View.INVISIBLE); - animateNavBarColorTo(0xff000000); - if (captionEditText.getTag() != null && keyboardVisible) { - if (isCurrentVideo) { - CharSequence title = muteVideo ? LocaleController.getString("GifCaption", R.string.GifCaption) : LocaleController.getString("VideoCaption", R.string.VideoCaption); - actionBarContainer.setTitleAnimated(title, true, false); - } else { - actionBarContainer.setTitleAnimated(LocaleController.getString("PhotoCaption", R.string.PhotoCaption), true, false); - } - - captionEditText.setAlpha(0f); - checkImageView.animate().alpha(0f).setDuration(220).start(); - photosCounterView.animate().alpha(0f).setDuration(220).start(); - selectedPhotosListView.animate().alpha(0.0f).translationY(-AndroidUtilities.dp(10)).setDuration(220).start(); - } else { - checkImageView.animate().alpha(1f).setDuration(220).start(); - photosCounterView.animate().alpha(1f).setDuration(220).start(); - if (lastTitle != null) { - if (!isCurrentVideo) { - actionBarContainer.setTitleAnimated(lastTitle, true, false); - lastTitle = null; - } - } - } - } - - @Override - protected void onTransitionEnd() { - super.onTransitionEnd(); - navigationBar.setVisibility(currentEditMode != EDIT_MODE_FILTER ? View.VISIBLE : View.INVISIBLE); - if (captionEditText.getTag() == null) { - captionEditText.setVisibility(View.GONE); - } - captionEditText.setTranslationY(0); - } - - @Override - protected boolean heightAnimationEnabled() { - return !captionEditText.isPopupShowing() && keyboardAnimationEnabled && currentEditMode != EDIT_MODE_PAINT; - } - }; - public FrameLayoutDrawer(Context context, Activity activity) { super(context, activity, false); setWillNotDraw(false); @@ -2767,10 +2833,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { ignoreLayout = false; } - measureChildWithMargins(captionEditText, widthMeasureSpec, 0, heightMeasureSpec, 0); - int inputFieldHeight = captionEditText.getMeasuredHeight(); - - final int bottomLayoutHeight = bottomLayout.getVisibility() != GONE ? AndroidUtilities.dp(48) : 0; + final int bottomLayoutHeight = bottomLayout.getVisibility() != GONE ? dp(48) : 0; final int groupedPhotosHeight; if (groupedPhotosListView != null && groupedPhotosListView.getVisibility() != GONE) { @@ -2805,7 +2868,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); - if (child.getVisibility() == GONE || child == captionEditText || child == groupedPhotosListView) { + if (child.getVisibility() == GONE || child == groupedPhotosListView) { continue; } if (child == aspectRatioFrameLayout) { @@ -2815,8 +2878,9 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width; int height; if (aspectRatioFrameLayout != null && aspectRatioFrameLayout.getVisibility() == VISIBLE) { - width = videoTextureView.getMeasuredWidth(); - height = videoTextureView.getMeasuredHeight(); + View view = usedSurfaceView ? videoSurfaceView : videoTextureView; + width = view.getMeasuredWidth(); + height = view.getMeasuredHeight(); } else { width = centerImage.getBitmapWidth(); height = centerImage.getBitmapHeight(); @@ -2826,17 +2890,18 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { height = heightSize; } paintingOverlay.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); - } else if (captionEditText.isPopupView(child)) { + } else if (captionEdit.editText.isPopupView(child)) { + int inputFieldHeight = 0; if (inBubbleMode) { child.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(heightSize - inputFieldHeight, MeasureSpec.EXACTLY)); } else if (AndroidUtilities.isInMultiwindow) { if (AndroidUtilities.isTablet()) { - child.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(Math.min(AndroidUtilities.dp(320), heightSize - inputFieldHeight - AndroidUtilities.statusBarHeight), MeasureSpec.EXACTLY)); + child.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(Math.min(dp(320), heightSize - inputFieldHeight - AndroidUtilities.statusBarHeight), MeasureSpec.EXACTLY)); } else { child.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(heightSize - inputFieldHeight - AndroidUtilities.statusBarHeight, MeasureSpec.EXACTLY)); } } else { - child.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(child.getLayoutParams().height, MeasureSpec.EXACTLY)); + child.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(child.getLayoutParams().height + AndroidUtilities.navigationBarHeight, MeasureSpec.EXACTLY)); } } else if (child == captionScrollView) { int bottomMargin = bottomLayoutHeight; @@ -2865,7 +2930,8 @@ protected void onLayout(boolean changed, int _l, int t, int _r, int _b) { final int count = getChildCount(); int keyboardHeight = measureKeyboardHeight(); keyboardSize = keyboardHeight; - int paddingBottom = keyboardHeight <= AndroidUtilities.dp(20) && !AndroidUtilities.isInMultiwindow ? captionEditText.getEmojiPadding() : 0; +// int paddingBottom = keyboardHeight <= dp(20) && !AndroidUtilities.isInMultiwindow ? captionEdit.editText.getEmojiPadding() : 0; + int paddingBottom = 0; for (int i = 0; i < count; i++) { final View child = getChildAt(i); @@ -2922,33 +2988,39 @@ protected void onLayout(boolean changed, int _l, int t, int _r, int _b) { break; } - if (child == mentionListView) { - childTop -= captionEditText.getMeasuredHeight(); - } else if (captionEditText.isPopupView(child)) { - if (AndroidUtilities.isInMultiwindow) { - childTop = captionEditText.getTop() - child.getMeasuredHeight() + AndroidUtilities.dp(1); - } else { - childTop = captionEditText.getBottom(); - } + if (child == captionEdit.mentionContainer) { + childTop -= captionEdit.getEditTextHeight(); + } else if (captionEdit.editText.isPopupView(child)) { + childTop = (_b - t) - height + (!inBubbleMode && !AndroidUtilities.isInMultiwindow ? AndroidUtilities.navigationBarHeight : 0); +// if (AndroidUtilities.isInMultiwindow) { +// childTop = captionEditText.getTop() - child.getMeasuredHeight() + dp(1); +// } else { +// childTop = captionEditText.getBottom(); +// } } else if (child == selectedPhotosListView) { - childTop = actionBar.getMeasuredHeight() + AndroidUtilities.dp(5); - } else if (child == cameraItem || child == muteItem) { + childTop = actionBar.getMeasuredHeight() + dp(5); + } else if (child == muteItem) { final int top; - if (videoTimelineView != null && videoTimelineView.getVisibility() == VISIBLE) { - top = videoTimelineView.getTop(); + if (videoTimelineViewContainer != null && videoTimelineViewContainer.getVisibility() == VISIBLE) { + top = videoTimelineViewContainer.getTop(); } else { top = pickerView.getTop(); } - childTop = top - AndroidUtilities.dp(sendPhotoType == 4 || sendPhotoType == 5 ? 40 : 15) - child.getMeasuredHeight(); - } else if (child == videoTimelineView) { + childTop = top - dp(sendPhotoType == 4 || sendPhotoType == 5 ? 40 : 15) - child.getMeasuredHeight(); + } else if (child == videoTimelineViewContainer) { childTop -= pickerView.getHeight(); if (sendPhotoType == SELECT_TYPE_AVATAR) { - childTop -= AndroidUtilities.dp(52); + childTop -= dp(52); + } else if (captionEdit.getVisibility() == View.VISIBLE) { + childTop -= dp(56); } + } else if (child == captionEditContainer) { + childTop = (b - t) - height - (lp.bottomMargin); + childTop -= pickerView.getHeight(); } else if (child == videoAvatarTooltip) { - childTop -= pickerView.getHeight() + AndroidUtilities.dp(31); + childTop -= pickerView.getHeight() + dp(31); } child.layout(childLeft + l, childTop, childLeft + width + l, childTop + height); } @@ -2970,8 +3042,8 @@ public void updateExclusionRects() { // 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)); + exclusionRects.add(new Rect(0, (h - dp(200)) / 2, dp(100), (h + dp(200)) / 2)); + exclusionRects.add(new Rect(w - dp(100), (h - dp(200)) / 2, w, (h + dp(200)) / 2)); } setSystemGestureExclusionRects(exclusionRects); invalidate(); @@ -3012,79 +3084,29 @@ public void draw(Canvas canvas) { } } - @Override - protected void dispatchDraw(Canvas canvas) { - canvas.save(); - canvas.clipRect(0, 0, getWidth(), getHeight()); - super.dispatchDraw(canvas); - canvas.restore(); - } - protected boolean drawChild(Canvas canvas, View child, long drawingTime) { if (child == leftPaintingOverlay || child == rightPaintingOverlay) { return false; } - if (child != navigationBar) { + if (child != navigationBar && (captionEdit == null || !captionEdit.editText.isPopupView(child))) { canvas.save(); - canvas.clipRect(0, 0, getWidth(), getHeight()); +// canvas.clipRect(0, 0, getWidth(), getHeight()); } boolean result = this.drawChildInternal(canvas, child, drawingTime); - if (child != navigationBar) { + if (child != navigationBar && (captionEdit == null || !captionEdit.editText.isPopupView(child))) { canvas.restore(); } return result; } protected boolean drawChildInternal(Canvas canvas, View child, long drawingTime) { - if (child == mentionListView || child == captionEditText) { - if (currentEditMode != EDIT_MODE_NONE && currentPanTranslationY == 0) { - return false; - } else if (AndroidUtilities.isInMultiwindow || AndroidUtilities.usingHardwareInput) { - if (!captionEditText.isPopupShowing() && captionEditText.getEmojiPadding() == 0 && captionEditText.getTag() == null) { - return false; - } - } else if (!captionEditText.isPopupShowing() && captionEditText.getEmojiPadding() == 0 && getKeyboardHeight() == 0) { - if (currentPanTranslationY == 0) { - return false; - } - } - if (child == mentionListView) { - canvas.save(); - canvas.clipRect(child.getX(), child.getY(), child.getX() + child.getWidth(), captionEditText.getTop()); - canvas.drawColor(0x7f000000); - boolean r = super.drawChild(canvas, child, drawingTime); - canvas.restore(); - return r; - } - } 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; - } else { - int paddingBottom = getKeyboardHeight() <= AndroidUtilities.dp(20) && !AndroidUtilities.isInMultiwindow ? captionEditText.getEmojiPadding() : 0; - if (captionEditText.isPopupShowing() || (AndroidUtilities.isInMultiwindow || AndroidUtilities.usingHardwareInput) && captionEditText.getTag() != null || getKeyboardHeight() > AndroidUtilities.dp(80) || paddingBottom != 0) { - bottomTouchEnabled = false; - return false; - } else { - bottomTouchEnabled = true; - } - } - } else if (child == checkImageView || child == photosCounterView) { - if (captionEditText.getTag() != null) { - bottomTouchEnabled = false; - if (child.getAlpha() < 0) { - return false; - } - } else { - bottomTouchEnabled = true; - } - } else if (child == miniProgressView) { + if (child == miniProgressView) { return false; } - if (child == videoTimelineView && videoTimelineView.getTranslationY() > 0 && pickerView.getTranslationY() == 0) { + if (child == videoTimelineViewContainer && videoTimelineViewContainer.getTranslationY() > 0 && pickerView.getTranslationY() == 0) { canvas.save(); - canvas.clipRect(videoTimelineView.getX(), videoTimelineView.getY(), videoTimelineView.getX() + videoTimelineView.getMeasuredWidth(), videoTimelineView.getBottom()); + canvas.clipRect(videoTimelineViewContainer.getX(), videoTimelineViewContainer.getY(), videoTimelineViewContainer.getX() + videoTimelineViewContainer.getMeasuredWidth(), videoTimelineViewContainer.getBottom()); boolean b = super.drawChild(canvas, child, drawingTime); canvas.restore(); return b; @@ -3107,17 +3129,24 @@ public void requestLayout() { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - adjustPanLayoutHelper.setResizableView(windowView); - adjustPanLayoutHelper.onAttach(); Bulletin.addDelegate(this, new Bulletin.Delegate() { @Override public int getBottomOffset(int tag) { int offset = 0; - if (bottomLayout != null && bottomLayout.getVisibility() == VISIBLE) { - offset += bottomLayout.getHeight(); - } - if (groupedPhotosListView != null && groupedPhotosListView.hasPhotos() && (AndroidUtilities.isTablet() || containerView.getMeasuredHeight() > containerView.getMeasuredWidth())) { - offset += groupedPhotosListView.getHeight(); + if (editing) { + if (captionEdit != null && captionEdit.getVisibility() == VISIBLE) { + offset += captionEdit.getEditTextHeight() + dp(12); + } + if (pickerView != null && pickerView.getVisibility() == VISIBLE) { + offset += pickerView.getHeight(); + } + } else { + if (bottomLayout != null && bottomLayout.getVisibility() == VISIBLE) { + offset += bottomLayout.getHeight(); + } + if (groupedPhotosListView != null && groupedPhotosListView.hasPhotos() && (AndroidUtilities.isTablet() || containerView.getMeasuredHeight() > containerView.getMeasuredWidth())) { + offset += groupedPhotosListView.getHeight(); + } } return offset; } @@ -3127,7 +3156,6 @@ public int getBottomOffset(int tag) { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - adjustPanLayoutHelper.onDetach(); Bulletin.removeDelegate(this); } @@ -3135,7 +3163,7 @@ protected void onDetachedFromWindow() { public void notifyHeightChanged() { super.notifyHeightChanged(); if (isCurrentVideo) { - photoProgressViews[0].setIndexedAlpha(2, getKeyboardHeight() <= AndroidUtilities.dp(20) ? 1.0f : 0.0f, true); + photoProgressViews[0].setIndexedAlpha(2, getKeyboardHeight() <= dp(20) ? 1.0f : 0.0f, true); } } } @@ -3188,12 +3216,12 @@ private class VideoPlayerControlFrameLayout extends FrameLayout { .addUpdateListener((animation, value, velocity) -> { int extraWidth; if (parentWidth > parentHeight) { - extraWidth = AndroidUtilities.dp(48); + extraWidth = dp(48); } else { extraWidth = 0; } - videoPlayerSeekbar.setSize((int) (getMeasuredWidth() - AndroidUtilities.dp(2 + 14) - value - extraWidth), getMeasuredHeight()); + videoPlayerSeekbar.setSize((int) (getMeasuredWidth() - dp(2 + 14) - value - extraWidth), getMeasuredHeight()); }); public VideoPlayerControlFrameLayout(@NonNull Context context) { @@ -3206,7 +3234,7 @@ public boolean onTouchEvent(MotionEvent event) { if (progress < 1f) { return false; } - if (videoPlayerSeekbar.onTouch(event.getAction(), event.getX() - AndroidUtilities.dp(2), event.getY())) { + if (videoPlayerSeekbar.onTouch(event.getAction(), event.getX() - dp(2), event.getY())) { getParent().requestDisallowInterceptTouchEvent(true); videoPlayerSeekbarView.invalidate(); return true; @@ -3239,14 +3267,14 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (exitFullscreenButton.getVisibility() != VISIBLE) { exitFullscreenButton.setVisibility(VISIBLE); } - extraWidth = AndroidUtilities.dp(48); - layoutParams.rightMargin = AndroidUtilities.dp(47); + extraWidth = dp(48); + layoutParams.rightMargin = dp(47); } else { if (exitFullscreenButton.getVisibility() != INVISIBLE) { exitFullscreenButton.setVisibility(INVISIBLE); } extraWidth = 0; - layoutParams.rightMargin = AndroidUtilities.dp(12); + layoutParams.rightMargin = dp(12); } ignoreLayout = false; super.onMeasure(widthMeasureSpec, heightMeasureSpec); @@ -3276,7 +3304,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { timeSpring.getSpring().setFinalPosition(size); timeSpring.start(); } else { - videoPlayerSeekbar.setSize(getMeasuredWidth() - AndroidUtilities.dp(2 + 14) - size - extraWidth, getMeasuredHeight()); + videoPlayerSeekbar.setSize(getMeasuredWidth() - dp(2 + 14) - size - extraWidth, getMeasuredHeight()); timeValue.setValue(size); } lastTimeWidth = size; @@ -3353,15 +3381,25 @@ public void setTranslationYAnimationEnabled(boolean translationYAnimationEnabled } } - private class CaptionTextViewSwitcher extends TextViewSwitcher { + public static class CaptionTextViewSwitcher extends TextViewSwitcher { private boolean inScrollView = false; private float alpha = 1.0f; + private NestedScrollView scrollView; + private FrameLayout container; public CaptionTextViewSwitcher(Context context) { super(context); } + public void setScrollView(NestedScrollView scrollView) { + this.scrollView = scrollView; + } + + public void setContainer(FrameLayout container) { + this.container = container; + } + @Override public void setVisibility(int visibility) { setVisibility(visibility, true); @@ -3370,7 +3408,7 @@ public void setVisibility(int visibility) { public void setVisibility(int visibility, boolean withScrollView) { super.setVisibility(visibility); if (inScrollView && withScrollView) { - captionScrollView.setVisibility(visibility); + scrollView.setVisibility(visibility); } } @@ -3378,7 +3416,7 @@ public void setVisibility(int visibility, boolean withScrollView) { public void setAlpha(float alpha) { this.alpha = alpha; if (inScrollView) { - captionScrollView.setAlpha(alpha); + scrollView.setAlpha(alpha); } else { super.setAlpha(alpha); } @@ -3397,17 +3435,17 @@ public float getAlpha() { public void setTranslationY(float translationY) { super.setTranslationY(translationY); if (inScrollView) { - captionScrollView.invalidate(); // invalidate background drawing + scrollView.invalidate(); // invalidate background drawing } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - if (captionContainer != null && getParent() == captionContainer) { + if (container != null && getParent() == container) { inScrollView = true; - captionScrollView.setVisibility(getVisibility()); - captionScrollView.setAlpha(alpha); + scrollView.setVisibility(getVisibility()); + scrollView.setAlpha(alpha); super.setAlpha(1.0f); } } @@ -3417,13 +3455,13 @@ protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (inScrollView) { inScrollView = false; - captionScrollView.setVisibility(View.GONE); + scrollView.setVisibility(View.GONE); super.setAlpha(alpha); } } } - private class CaptionScrollView extends NestedScrollView { + public static class CaptionScrollView extends NestedScrollView { private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -3441,17 +3479,23 @@ private class CaptionScrollView extends NestedScrollView { private int textHash; private int prevHeight; - private float backgroundAlpha = 1f; - private boolean dontChangeTopMargin; + public float backgroundAlpha = 1f; + public boolean dontChangeTopMargin; private int pendingTopMargin = -1; - public CaptionScrollView(@NonNull Context context) { + private final CaptionTextViewSwitcher captionTextViewSwitcher; + private final FrameLayout captionContainer; + + public CaptionScrollView(@NonNull Context context, CaptionTextViewSwitcher switcher, FrameLayout container) { super(context); + this.captionTextViewSwitcher = switcher; + this.captionContainer = container; + setClipChildren(false); setOverScrollMode(View.OVER_SCROLL_NEVER); paint.setColor(Color.BLACK); - setFadingEdgeLength(AndroidUtilities.dp(12)); + setFadingEdgeLength(dp(12)); setVerticalFadingEdgeEnabled(true); setWillNotDraw(false); @@ -3461,6 +3505,10 @@ public CaptionScrollView(@NonNull Context context) { springAnimation.addUpdateListener((animation, value, velocity) -> { overScrollY = value; velocityY = velocity; + onScrollUpdate(); + }); + springAnimation.addEndListener((anm, c, v, a) -> { + onScrollEnd(); }); springAnimation.getSpring().setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY); @@ -3570,7 +3618,7 @@ public int calculateNewContainerMarginTop(int width, int height) { } final int lineHeight = textView.getPaint().getFontMetricsInt(null); - return height - lineHeight * i - AndroidUtilities.dp(8); + return height - lineHeight * i - dp(8); } public void reset() { @@ -3618,6 +3666,7 @@ public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] off consumed[1] += dy; } } + onScrollUpdate(); captionTextViewSwitcher.setTranslationY(overScrollY); return true; @@ -3656,6 +3705,8 @@ public void dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsume captionTextViewSwitcher.setTranslationY(overScrollY); } } + + onScrollUpdate(); } } @@ -3672,6 +3723,7 @@ public boolean startNestedScroll(int axes, int type) { springAnimation.cancel(); nestedScrollStarted = true; overScrollY = captionTextViewSwitcher.getTranslationY(); + onScrollStart(); } return true; } @@ -3682,6 +3734,7 @@ public void computeScroll() { if (!nestedScrollStarted && overScrollY != 0 && scroller != null && scroller.isFinished()) { startSpringAnimationIfNotRunning(0); } + onScrollUpdate(); } @Override @@ -3691,9 +3744,14 @@ public void stopNestedScroll(int type) { if (overScrollY != 0 && scroller != null && scroller.isFinished()) { startSpringAnimationIfNotRunning(velocityY); } + onScrollEnd(); } } + protected void onScrollStart() {} + protected void onScrollUpdate() {} + protected void onScrollEnd() {} + @Override protected float getTopFadingEdgeStrength() { return 1f; @@ -3720,41 +3778,8 @@ public void draw(Canvas canvas) { canvas.restoreToCount(saveCount); } - @Override - public void invalidate() { - super.invalidate(); - if (isActionBarVisible) { - final int scrollY = getScrollY(); - final float translationY = captionTextViewSwitcher.getTranslationY(); - - boolean buttonVisible = scrollY == 0 && translationY == 0; - boolean enalrgeIconVisible = scrollY == 0 && translationY == 0; - - if (!buttonVisible) { - final int progressBottom = photoProgressViews[0].getY() + photoProgressViews[0].size; - final int topMargin = (isStatusBarVisible() ? AndroidUtilities.statusBarHeight : 0) + ActionBar.getCurrentActionBarHeight(); - final int captionTop = captionContainer.getTop() + (int) translationY - scrollY + topMargin - AndroidUtilities.dp(12); - final int enlargeIconTop = (int) fullscreenButton[0].getY(); - enalrgeIconVisible = captionTop > enlargeIconTop + AndroidUtilities.dp(32); - buttonVisible = captionTop > progressBottom; - } - if (allowShowFullscreenButton) { - if (fullscreenButton[0].getTag() != null && ((Integer) fullscreenButton[0].getTag()) == 3 && enalrgeIconVisible) { - fullscreenButton[0].setTag(2); - fullscreenButton[0].animate().alpha(1).setDuration(150).setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - fullscreenButton[0].setTag(null); - } - }).start(); - } else if (fullscreenButton[0].getTag() == null && !enalrgeIconVisible) { - fullscreenButton[0].setTag(3); - fullscreenButton[0].animate().alpha(0).setListener(null).setDuration(150).start(); - } - - } - photoProgressViews[0].setIndexedAlpha(2, buttonVisible ? 1f : 0f, true); - } + protected boolean isStatusBarVisible() { + return true; } } @@ -4323,8 +4348,8 @@ public void setParentActivity(Activity inActivity, BaseFragment fragment, Theme. centerImage.setCurrentAccount(currentAccount); leftImage.setCurrentAccount(currentAccount); rightImage.setCurrentAccount(currentAccount); - if (captionEditText != null) { - captionEditText.currentAccount = UserConfig.selectedAccount; + if (captionEdit != null) { + captionEdit.setAccount(currentAccount); } if (parentActivity == activity || activity == null) { updateColors(); @@ -4348,8 +4373,6 @@ public void setParentActivity(Activity inActivity, BaseFragment fragment, Theme. windowView = new FrameLayout(activity) { - private Runnable attachRunnable; - @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return isVisible && super.onInterceptTouchEvent(ev); @@ -4402,8 +4425,7 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); - if (Build.VERSION.SDK_INT >= 21 && lastInsets != null) { - WindowInsets insets = (WindowInsets) lastInsets; + if (Build.VERSION.SDK_INT >= 21) { if (!inBubbleMode) { if (AndroidUtilities.incorrectDisplaySizeFix) { if (heightSize > AndroidUtilities.displaySize.y) { @@ -4411,21 +4433,22 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } heightSize += AndroidUtilities.statusBarHeight; } else { - int insetBottom = insets.getStableInsetBottom(); + int insetBottom = insets.bottom; if (insetBottom >= 0 && AndroidUtilities.statusBarHeight >= 0) { - int newSize = heightSize - AndroidUtilities.statusBarHeight - insets.getStableInsetBottom(); + int newSize = heightSize - AndroidUtilities.statusBarHeight - insets.bottom; if (newSize > 0 && newSize < 4096) { AndroidUtilities.displaySize.y = newSize; } } } } - int bottomInsets = insets.getSystemWindowInsetBottom(); - if (captionEditText.isPopupShowing()) { - bottomInsets -= containerView.getKeyboardHeight(); - } + int bottomInsets = insets.bottom; heightSize -= bottomInsets; } else { + if (Build.VERSION.SDK_INT < 21) { + insets.top = AndroidUtilities.statusBarHeight; + insets.bottom = AndroidUtilities.navigationBarHeight; + } if (heightSize > AndroidUtilities.displaySize.y) { heightSize = AndroidUtilities.displaySize.y; } @@ -4459,14 +4482,14 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto LayoutParams layoutParams = (LayoutParams) checkImageView.getLayoutParams(); WindowManager manager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Activity.WINDOW_SERVICE); int rotation = manager.getDefaultDisplay().getRotation(); - int newMargin = (ActionBar.getCurrentActionBarHeight() - AndroidUtilities.dp(34)) / 2 + (isStatusBarVisible() ? AndroidUtilities.statusBarHeight : 0); + int newMargin = (ActionBar.getCurrentActionBarHeight() - dp(34)) / 2 + (isStatusBarVisible() ? AndroidUtilities.statusBarHeight : 0); if (newMargin != layoutParams.topMargin) { layoutParams.topMargin = newMargin; checkImageView.setLayoutParams(layoutParams); } layoutParams = (LayoutParams) photosCounterView.getLayoutParams(); - newMargin = (ActionBar.getCurrentActionBarHeight() - AndroidUtilities.dp(40)) / 2 + (isStatusBarVisible() ? AndroidUtilities.statusBarHeight : 0); + newMargin = (ActionBar.getCurrentActionBarHeight() - dp(40)) / 2 + (isStatusBarVisible() ? AndroidUtilities.statusBarHeight : 0); if (layoutParams.topMargin != newMargin) { layoutParams.topMargin = newMargin; photosCounterView.setLayoutParams(layoutParams); @@ -4505,7 +4528,7 @@ public boolean dispatchKeyEventPreIme(KeyEvent event) { if (textSelectionHelper.isInSelectionMode()) { textSelectionHelper.clear(); } - if (captionEditText.isPopupShowing() || captionEditText.isKeyboardVisible()) { + if (isCaptionOpen()) { closeCaptionEnter(true); return false; } @@ -4517,10 +4540,9 @@ public boolean dispatchKeyEventPreIme(KeyEvent event) { @Override protected void onDraw(Canvas canvas) { - if (Build.VERSION.SDK_INT >= 21 && isVisible && lastInsets != null) { - WindowInsets insets = (WindowInsets) lastInsets; + if (Build.VERSION.SDK_INT >= 21 && isVisible) { blackPaint.setAlpha(backgroundDrawable.getAlpha()); - canvas.drawRect(0, getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight() + insets.getSystemWindowInsetBottom(), blackPaint); + canvas.drawRect(0, getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight() + insets.bottom, blackPaint); } } @@ -4552,13 +4574,18 @@ protected void dispatchDraw(Canvas canvas) { private Bulletin.Delegate delegate = new Bulletin.Delegate() { @Override public int getBottomOffset(int tag) { - if (captionEditText.getVisibility() == View.GONE) { + if (captionEdit.editText.getVisibility() == View.GONE) { return 0; } - return getHeight() - captionEditText.getTop(); + return getHeight() - captionEdit.editText.getTop(); } }; + @Override + public int getBottomPadding() { + return pickerView.getHeight(); + } + @Override public boolean dispatchTouchEvent(MotionEvent ev) { textSelectionHelper.getOverlayView(getContext()).checkCancelAction(ev); @@ -4608,18 +4635,33 @@ protected void dispatchDraw(Canvas canvas) { windowView.setClipChildren(false); windowView.setClipToPadding(false); + blurManager = new BlurringShader.BlurManager(containerView); + blurManager.padding = 1; + + shadowBlurer = new BlurringShader.StoryBlurDrawer(blurManager, containerView, BlurringShader.StoryBlurDrawer.BLUR_TYPE_SHADOW); + windowView.addView(containerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); if (Build.VERSION.SDK_INT >= 21) { containerView.setFitsSystemWindows(true); - containerView.setOnApplyWindowInsetsListener((v, insets) -> { - int newTopInset = insets.getSystemWindowInsetTop(); + containerView.setOnApplyWindowInsetsListener((v, newInsets) -> { + final Rect oldInsets = new Rect(insets); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + final Insets r = newInsets.getInsets(WindowInsetsCompat.Type.displayCutout() | WindowInsetsCompat.Type.systemBars()); + insets.set(r.left, r.top, r.right, r.bottom); + } else { + insets.set( + newInsets.getStableInsetLeft(), + newInsets.getStableInsetTop(), + newInsets.getStableInsetRight(), + newInsets.getStableInsetBottom() + ); + } + int newTopInset = insets.top; if (parentActivity instanceof LaunchActivity && (newTopInset != 0 || AndroidUtilities.isInMultiwindow) && !inBubbleMode && AndroidUtilities.statusBarHeight != newTopInset) { AndroidUtilities.statusBarHeight = newTopInset; ((LaunchActivity) parentActivity).drawerLayoutContainer.requestLayout(); } - WindowInsets oldInsets = (WindowInsets) lastInsets; - lastInsets = insets; - if (oldInsets == null || !oldInsets.toString().equals(insets.toString())) { + if (!oldInsets.equals(newInsets)) { if (animationInProgress == 1 || animationInProgress == 3) { animatingImageView.setTranslationX(animatingImageView.getTranslationX() - getLeftInset()); animationValues[0][2] = animatingImageView.getTranslationX(); @@ -4630,23 +4672,23 @@ protected void dispatchDraw(Canvas canvas) { } if (navigationBar != null) { - navigationBarHeight = insets.getSystemWindowInsetBottom(); + navigationBarHeight = insets.bottom; ViewGroup.MarginLayoutParams navigationBarLayoutParams = (ViewGroup.MarginLayoutParams) navigationBar.getLayoutParams(); navigationBarLayoutParams.height = navigationBarHeight; navigationBarLayoutParams.bottomMargin = -navigationBarHeight / 2; navigationBar.setLayoutParams(navigationBarLayoutParams); } - containerView.setPadding(insets.getSystemWindowInsetLeft(), 0, insets.getSystemWindowInsetRight(), 0); + containerView.setPadding(insets.left, 0, insets.right, 0); if (actionBar != null) { AndroidUtilities.cancelRunOnUIThread(updateContainerFlagsRunnable); if (isVisible && animationInProgress == 0) { AndroidUtilities.runOnUIThread(updateContainerFlagsRunnable, 200); } } - if (Build.VERSION.SDK_INT >= 30) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { return WindowInsets.CONSUMED; } else { - return insets.consumeSystemWindowInsets(); + return newInsets.consumeSystemWindowInsets(); } }); containerView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); @@ -4706,7 +4748,7 @@ public void onItemClick(int id) { if (photoPaintView != null && photoPaintView.onBackPressed()) { return; } - if (needCaptionLayout && (captionEditText.isPopupShowing() || captionEditText.isKeyboardVisible())) { + if (isCaptionOpen()) { closeCaptionEnter(false); return; } @@ -5112,7 +5154,7 @@ public void onItemClick(int id) { } else { cell.setText(LocaleController.formatString("DeleteForUser", R.string.DeleteForUser, UserObject.getFirstName(currentUser)), "", false, false); } - cell.setPadding(LocaleController.isRTL ? AndroidUtilities.dp(16) : AndroidUtilities.dp(8), 0, LocaleController.isRTL ? AndroidUtilities.dp(8) : AndroidUtilities.dp(16), 0); + cell.setPadding(LocaleController.isRTL ? dp(16) : dp(8), 0, LocaleController.isRTL ? dp(8) : dp(16), 0); frameLayout.addView(cell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.TOP | Gravity.LEFT, 0, 0, 0, 0)); cell.setOnClickListener(v -> { CheckBoxCell cell1 = (CheckBoxCell) v; @@ -5631,7 +5673,12 @@ public void onHideSubMenu() { } }); - bottomLayout = new FrameLayout(activityContext); + bottomLayout = new FrameLayout(activityContext) { + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + } + }; bottomLayout.setBackgroundColor(0x7f000000); containerView.addView(bottomLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM | Gravity.LEFT)); @@ -5644,7 +5691,7 @@ public void onHideSubMenu() { pressedDrawable[1] = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, new int[]{0x32000000, 0}); pressedDrawable[1].setShape(GradientDrawable.RECTANGLE); - groupedPhotosListView = new GroupedPhotosListView(activityContext, AndroidUtilities.dp(10)); + groupedPhotosListView = new GroupedPhotosListView(activityContext, dp(10)); containerView.addView(groupedPhotosListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 68, Gravity.BOTTOM | Gravity.LEFT)); groupedPhotosListView.setDelegate(new GroupedPhotosListView.GroupedPhotosListViewDelegate() { @Override @@ -5751,8 +5798,15 @@ public boolean validGroupId(long groupId) { }); } + textSelectionHelper = new TextSelectionHelper.SimpleTextSelectionHelper(null, new DarkThemeResourceProvider()) { + @Override + public int getParentBottomPadding() { + return 0;//AndroidUtilities.dp(80); + } + }; + captionTextViewSwitcher = new CaptionTextViewSwitcher(containerView.getContext()); - captionTextViewSwitcher.setFactory(() -> new CaptionTextView(activityContext)); + captionTextViewSwitcher.setFactory(() -> new CaptionTextView(activityContext, captionScrollView, textSelectionHelper, this::onLinkClick, this::onLinkLongPress)); captionTextViewSwitcher.setVisibility(View.INVISIBLE); setCaptionHwLayerEnabled(true); @@ -5794,109 +5848,12 @@ public void invalidate() { }; miniProgressView.setUseSelfAlpha(true); miniProgressView.setProgressColor(0xffffffff); - miniProgressView.setSize(AndroidUtilities.dp(54)); + miniProgressView.setSize(dp(54)); miniProgressView.setBackgroundResource(R.drawable.circle_big); miniProgressView.setVisibility(View.INVISIBLE); 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.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(); progressView = new RadialProgressView(parentActivity, resourcesProvider); @@ -5908,7 +5865,7 @@ public void invalidate() { qualityPicker = new PickerBottomLayoutViewer(parentActivity); qualityPicker.setBackgroundColor(0x7f000000); qualityPicker.updateSelectedCount(0, false); - qualityPicker.setTranslationY(AndroidUtilities.dp(120)); + qualityPicker.setTranslationY(dp(120)); qualityPicker.doneButton.setText(LocaleController.getString("Done", R.string.Done).toUpperCase()); qualityPicker.doneButton.setTextColor(getThemedColor(Theme.key_dialogFloatingButton)); containerView.addView(qualityPicker, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM | Gravity.LEFT)); @@ -5941,7 +5898,7 @@ public void invalidate() { }); qualityChooseView = new QualityChooseView(parentActivity); - qualityChooseView.setTranslationY(AndroidUtilities.dp(120)); + qualityChooseView.setTranslationY(dp(120)); qualityChooseView.setVisibility(View.INVISIBLE); qualityChooseView.setBackgroundColor(0x7f000000); containerView.addView(qualityChooseView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 70, Gravity.LEFT | Gravity.BOTTOM, 0, 0, 0, 48)); @@ -5950,43 +5907,47 @@ public void invalidate() { pickerBackgroundPaint.setColor(0x7f000000); pickerView = new FrameLayout(activityContext) { + private final Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + private final LinearGradient bgGradient = new LinearGradient(0, 0, 0, 16, new int[] { 0, 0x7f000000 }, new float[] { 0, 1 }, Shader.TileMode.CLAMP); + private final Matrix bgMatrix = new Matrix(); + @Override protected void dispatchDraw(Canvas canvas) { - if (doneButtonFullWidth.getVisibility() == View.VISIBLE) { - canvas.drawRect(0, getMeasuredHeight() - AndroidUtilities.dp(48), getMeasuredWidth(), getMeasuredHeight(), pickerBackgroundPaint); - } else { - canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), pickerBackgroundPaint); + if (!fancyShadows) { + int top = 0; + if (doneButtonFullWidth.getVisibility() == View.VISIBLE) { + top = getMeasuredHeight() - dp(48); + } + if (sendPhotoType == 0 || sendPhotoType == 2 || sendPhotoType == SELECT_TYPE_NO_SELECT) { + bgMatrix.reset(); + final float gradientHeight = Math.min(dp(40), getMeasuredHeight() - top); + bgMatrix.postTranslate(0, top); + bgMatrix.postScale(1, gradientHeight / 16f); + bgGradient.setLocalMatrix(bgMatrix); + bgPaint.setShader(bgGradient); + } else { + bgPaint.setShader(null); + bgPaint.setColor(0x7f000000); + } + canvas.drawRect(0, top, getMeasuredWidth(), getMeasuredHeight(), bgPaint); } super.dispatchDraw(canvas); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - ((LayoutParams) itemsLayout.getLayoutParams()).rightMargin = pickerViewSendButton.getVisibility() == View.VISIBLE ? AndroidUtilities.dp(70) : 0; + ((LayoutParams) itemsLayout.getLayoutParams()).rightMargin = pickerViewSendButton.getVisibility() == View.VISIBLE ? dp(70) : 0; super.onMeasure(widthMeasureSpec, heightMeasureSpec); } - @Override - public boolean dispatchTouchEvent(MotionEvent ev) { - return bottomTouchEnabled && super.dispatchTouchEvent(ev); - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - return bottomTouchEnabled && super.onInterceptTouchEvent(ev); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - return bottomTouchEnabled && super.onTouchEvent(event); - } - @Override public void setTranslationY(float translationY) { super.setTranslationY(translationY); - if (videoTimelineView != null && videoTimelineView.getVisibility() != GONE) { - videoTimelineView.setTranslationY(translationY); - videoAvatarTooltip.setTranslationY(translationY); + if (videoTimelineViewContainer != null && videoTimelineViewContainer.getVisibility() != GONE) { + videoTimelineViewContainer.setTranslationY(translationY - Math.max(0, captionEdit.getEditTextHeight() - dp(46))); + } + if (captionEditContainer != null) { + captionEditContainer.setTranslationY(translationY); } if (videoAvatarTooltip != null && videoAvatarTooltip.getVisibility() != GONE) { videoAvatarTooltip.setTranslationY(translationY); @@ -5996,16 +5957,19 @@ public void setTranslationY(float translationY) { @Override public void setAlpha(float alpha) { super.setAlpha(alpha); - if (videoTimelineView != null && videoTimelineView.getVisibility() != GONE) { - videoTimelineView.setAlpha(alpha); + if (videoTimelineViewContainer != null && videoTimelineViewContainer.getVisibility() != GONE) { + videoTimelineViewContainer.setAlpha(alpha); + } + if (captionEdit != null && captionEdit.getVisibility() != GONE) { + captionEdit.setAlpha(alpha); } } @Override public void setVisibility(int visibility) { super.setVisibility(visibility); - if (videoTimelineView != null && videoTimelineView.getVisibility() != GONE) { - videoTimelineView.setVisibility(visibility == VISIBLE ? VISIBLE : INVISIBLE); + if (videoTimelineViewContainer != null && videoTimelineViewContainer.getVisibility() != GONE) { + videoTimelineViewContainer.setVisibility(visibility == VISIBLE ? VISIBLE : INVISIBLE); } } @@ -6013,7 +5977,7 @@ public void setVisibility(int visibility) { protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (itemsLayout.getVisibility() != GONE) { - int rightMargin = pickerViewSendButton.getVisibility() == View.VISIBLE ? AndroidUtilities.dp(70) : 0; + int rightMargin = pickerViewSendButton.getVisibility() == View.VISIBLE ? dp(70) : 0; int x = (right - left - rightMargin - itemsLayout.getMeasuredWidth()) / 2; itemsLayout.layout(x, itemsLayout.getTop(), x + itemsLayout.getMeasuredWidth(), itemsLayout.getTop() + itemsLayout.getMeasuredHeight()); } @@ -6063,6 +6027,31 @@ public void setTranslationY(float translationY) { containerView.invalidate(); } } + + private final Path path = new Path(); + private final BlurringShader.StoryBlurDrawer blur = new BlurringShader.StoryBlurDrawer(blurManager, this, BlurringShader.StoryBlurDrawer.BLUR_TYPE_BACKGROUND); + + @Override + protected boolean customBlur() { + return true; + } + + @Override + protected void drawBlur(Canvas canvas, RectF rect) { + canvas.save(); + canvas.clipRect(rect); + canvas.translate(-getX() - videoTimelineViewContainer.getX(), -getY() - videoTimelineViewContainer.getY()); + drawCaptionBlur(canvas, blur, 0xff1e1e1e, 0x33000000, false, true, false); + canvas.restore(); + } + + @Override + public void invalidate() { + if (SharedConfig.photoViewerBlur && (animationInProgress == 1 || animationInProgress == 2 || animationInProgress == 3)) { + return; + } + super.invalidate(); + } }; videoTimelineView.setDelegate(new VideoTimelinePlayView.VideoTimelineViewDelegate() { @@ -6197,69 +6186,133 @@ public void didStopDragging(int type) { } } }); + videoTimelineViewContainer = new FrameLayout(parentActivity); + videoTimelineViewContainer.setClipChildren(false); + videoTimelineViewContainer.addView(videoTimelineView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 54, Gravity.LEFT | Gravity.BOTTOM)); showVideoTimeline(false, false); - videoTimelineView.setBackgroundColor(0x7f000000); - containerView.addView(videoTimelineView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 58, Gravity.LEFT | Gravity.BOTTOM, 0, 8, 0, 0)); + containerView.addView(videoTimelineViewContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 54, Gravity.LEFT | Gravity.BOTTOM, 0, 8, 0, 0)); - videoAvatarTooltip = new TextView(parentActivity); - videoAvatarTooltip.setSingleLine(true); - videoAvatarTooltip.setVisibility(View.GONE); - videoAvatarTooltip.setText(LocaleController.getString("ChooseCover", R.string.ChooseCover)); - videoAvatarTooltip.setGravity(Gravity.CENTER_HORIZONTAL); - videoAvatarTooltip.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - videoAvatarTooltip.setTextColor(0xff8c8c8c); - containerView.addView(videoAvatarTooltip, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 0, 8, 0, 0)); + captionEdit = new CaptionPhotoViewer(containerView.getContext(), windowView, containerView, containerView, resourcesProvider, blurManager, this::applyCaption) { + private final Path path = new Path(); - pickerViewSendButton = new ImageView(parentActivity) { @Override - public boolean dispatchTouchEvent(MotionEvent ev) { - return bottomTouchEnabled && super.dispatchTouchEvent(ev); + protected boolean customBlur() { + return true; } @Override - public boolean onTouchEvent(MotionEvent event) { - return bottomTouchEnabled && super.onTouchEvent(event); + protected boolean ignoreTouches() { + return !keyboardShown && currentEditMode != EDIT_MODE_NONE; } @Override - public void setVisibility(int visibility) { - super.setVisibility(visibility); - if (captionEditText.getCaptionLimitOffset() < 0) { - captionLimitView.setVisibility(visibility); + protected void drawBlur(BlurringShader.StoryBlurDrawer blur, Canvas canvas, RectF rect, float r, boolean text, float ox, float oy, boolean thisView) { + canvas.save(); + path.rewind(); + path.addRoundRect(rect, r, r, Path.Direction.CW); + canvas.clipPath(path); + if (thisView) { + canvas.translate(-getX() - captionEditContainer.getX() + ox, -getY() - captionEditContainer.getY() + oy); } else { - captionLimitView.setVisibility(View.GONE); + canvas.translate(ox, oy); } + drawCaptionBlur(canvas, blur, text ? 0xFF787878 : 0xFF262626, thisView ? (text ? 0 : 0x33000000) : 0x44000000, false, !text, !text && thisView); + canvas.restore(); } @Override - public void setTranslationY(float translationY) { - super.setTranslationY(translationY); - captionLimitView.setTranslationY(translationY); + protected boolean captionLimitToast() { + if (limitBulletin != null && Bulletin.getVisibleBulletin() == limitBulletin) { + return false; + } + return showCaptionLimitBulletin(containerView); } @Override - public void setAlpha(float alpha) { - super.setAlpha(alpha); - captionLimitView.setAlpha(alpha); + protected void setupMentionContainer() { + if (parentChatActivity != null) { + mentionContainer.getAdapter().setChatInfo(parentChatActivity.chatInfo); + mentionContainer.getAdapter().setNeedUsernames(parentChatActivity.currentChat != null); + } else { + mentionContainer.getAdapter().setChatInfo(null); + mentionContainer.getAdapter().setNeedUsernames(false); + } + mentionContainer.getAdapter().setNeedBotContext(false); + } + + @Override + protected void onUpdateShowKeyboard(float keyboardT) { + super.onUpdateShowKeyboard(keyboardT); + muteItem.setAlpha((1f - keyboardT) * (muteItem.getTag() != null ? 1 : 0)); + videoTimelineViewContainer.setAlpha((1f - keyboardT) * (videoTimelineViewContainer.getTag() != null ? 1 : 0)); + } + + @Override + public void invalidate() { + if (SharedConfig.photoViewerBlur && (animationInProgress == 1 || animationInProgress == 2 || animationInProgress == 3)) { + return; + } + super.invalidate(); + } + }; + captionEdit.setOnTimerChange(seconds -> { + Object object1 = imagesArrLocals.get(currentIndex); + if (object1 instanceof MediaController.PhotoEntry) { + ((MediaController.PhotoEntry) object1).ttl = seconds; + } else if (object1 instanceof MediaController.SearchImage) { + ((MediaController.SearchImage) object1).ttl = seconds; + } + if (seconds != 0 && !placeProvider.isPhotoChecked(currentIndex)) { + setPhotoChecked(); + } + }); + captionEdit.setAccount(currentAccount); + captionEdit.setOnHeightUpdate(height -> { + videoTimelineViewContainer.setTranslationY(pickerView.getTranslationY() - Math.max(0, height - dp(46))); + muteItem.setTranslationY(-Math.max(0, height - dp(46))); + }); + captionEdit.setOnAddPhotoClick(v -> { + if (placeProvider == null || isCaptionOpen()) { + return; + } + placeProvider.needAddMorePhotos(); + closePhoto(true, false); + }); + showEditCaption(false, false); + + captionEditContainer = new FrameLayout(parentActivity) { + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + invalidateBlur(); } }; + captionEditContainer.addView(captionEdit, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.BOTTOM)); + containerView.addView(captionEditContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.BOTTOM, 0, 8, 0, 0)); + + videoAvatarTooltip = new TextView(parentActivity); + videoAvatarTooltip.setSingleLine(true); + videoAvatarTooltip.setVisibility(View.GONE); + videoAvatarTooltip.setText(LocaleController.getString("ChooseCover", R.string.ChooseCover)); + videoAvatarTooltip.setGravity(Gravity.CENTER_HORIZONTAL); + videoAvatarTooltip.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + videoAvatarTooltip.setTextColor(0xff8c8c8c); + containerView.addView(videoAvatarTooltip, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 0, 8, 0, 0)); + + pickerViewSendButton = new ImageView(parentActivity); pickerViewSendButton.setScaleType(ImageView.ScaleType.CENTER); - pickerViewSendDrawable = Theme.createSimpleSelectorCircleDrawable(AndroidUtilities.dp(56), getThemedColor(Theme.key_dialogFloatingButton), getThemedColor(Build.VERSION.SDK_INT >= 21 ? Theme.key_dialogFloatingButtonPressed : Theme.key_dialogFloatingButton)); + pickerViewSendDrawable = Theme.createSimpleSelectorCircleDrawable(dp(48), getThemedColor(Theme.key_dialogFloatingButton), getThemedColor(Build.VERSION.SDK_INT >= 21 ? Theme.key_dialogFloatingButtonPressed : Theme.key_dialogFloatingButton)); pickerViewSendButton.setBackgroundDrawable(pickerViewSendDrawable); - pickerViewSendButton.setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.SRC_IN)); - pickerViewSendButton.setImageResource(R.drawable.attach_send); - pickerViewSendButton.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_dialogFloatingIcon), PorterDuff.Mode.SRC_IN)); - containerView.addView(pickerViewSendButton, LayoutHelper.createFrame(56, 56, Gravity.RIGHT | Gravity.BOTTOM, 0, 0, 14, 14)); + pickerViewSendButton.setImageResource(R.drawable.msg_input_send_mini); + pickerViewSendButton.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_dialogFloatingIcon), PorterDuff.Mode.MULTIPLY)); + containerView.addView(pickerViewSendButton, LayoutHelper.createFrame(48, 48, Gravity.RIGHT | Gravity.BOTTOM, 0, 0, 14, 2.33f)); pickerViewSendButton.setContentDescription(LocaleController.getString("Send", R.string.Send)); + ScaleStateListAnimator.apply(pickerViewSendButton); pickerViewSendButton.setOnClickListener(v -> { - if (captionEditText.getCaptionLimitOffset() < 0) { - AndroidUtilities.shakeView(captionLimitView); - try { - if (!NekoConfig.disableVibration.Bool()) - captionLimitView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); - } catch (Exception ignored) {} - - if (!MessagesController.getInstance(currentAccount).premiumLocked && MessagesController.getInstance(currentAccount).captionLengthLimitPremium > captionEditText.getCodePointCount()) { + if (captionEdit.isCaptionOverLimit()) { + AndroidUtilities.shakeViewSpring(captionEdit.limitTextView, shiftDp = -shiftDp); + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + if (!MessagesController.getInstance(currentAccount).premiumLocked && MessagesController.getInstance(currentAccount).captionLengthLimitPremium > captionEdit.getCodePointCount()) { showCaptionLimitBulletin(containerView); } return; @@ -6278,7 +6331,7 @@ public void setAlpha(float alpha) { if (!isStoryViewer && (parentChatActivity == null || parentChatActivity.isInScheduleMode())) { return false; } - if (captionEditText.getCaptionLimitOffset() < 0) { + if (captionEdit.isCaptionOverLimit()) { return false; } TLRPC.Chat chat = parentChatActivity.getCurrentChat(); @@ -6349,7 +6402,7 @@ public void setAlpha(float alpha) { continue; } else if ((a == 2 || a == 3) && !canReplace) { continue; - } else if (a == 4 && (isCurrentVideo || timeItem.getColorFilter() != null)) { + } else if (a == 4 && (isCurrentVideo || captionEdit.hasTimer())) { continue; } else if (a == 6 && !canSpoiler) { continue; @@ -6378,7 +6431,7 @@ public void setAlpha(float alpha) { } else if (a == 6) { cell.setTextAndIcon(LocaleController.getString(spoilerEnabled ? R.string.DisablePhotoSpoiler : R.string.EnablePhotoSpoiler), spoilerEnabled ? R.drawable.msg_spoiler_off : R.drawable.msg_spoiler); } - cell.setMinimumWidth(AndroidUtilities.dp(196)); + cell.setMinimumWidth(dp(196)); cell.setColors(0xffffffff, 0xffffffff); sendPopupLayout.addView(cell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); long chatId; @@ -6443,27 +6496,18 @@ public void setAlpha(float alpha) { sendPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED); sendPopupWindow.getContentView().setFocusableInTouchMode(true); - sendPopupLayout.measure(View.MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(1000), View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(1000), View.MeasureSpec.AT_MOST)); + sendPopupLayout.measure(View.MeasureSpec.makeMeasureSpec(dp(1000), View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(dp(1000), View.MeasureSpec.AT_MOST)); sendPopupWindow.setFocusable(true); int[] location = new int[2]; view.getLocationInWindow(location); - sendPopupWindow.showAtLocation(view, Gravity.LEFT | Gravity.TOP, location[0] + view.getMeasuredWidth() - sendPopupLayout.getMeasuredWidth() + AndroidUtilities.dp(14), location[1] - sendPopupLayout.getMeasuredHeight() - AndroidUtilities.dp(18)); - if (!NekoConfig.disableVibration.Bool()) { - if (!NekoConfig.disableVibration.Bool()) - view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); - } + sendPopupWindow.showAtLocation(view, Gravity.LEFT | Gravity.TOP, location[0] + view.getMeasuredWidth() - sendPopupLayout.getMeasuredWidth() + dp(14), location[1] - sendPopupLayout.getMeasuredHeight() - dp(18)); + if (!NekoConfig.disableVibration.Bool()) + view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); return false; }); - captionLimitView = new TextView(parentActivity); - captionLimitView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); - captionLimitView.setTextColor(0xffEC7777); - captionLimitView.setGravity(Gravity.CENTER); - captionLimitView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - containerView.addView(captionLimitView, LayoutHelper.createFrame(56, 20, Gravity.BOTTOM | Gravity.RIGHT, 3, 0, 14, 78)); - itemsLayout = new LinearLayout(parentActivity) { boolean ignoreLayout; @@ -6483,7 +6527,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int height = MeasureSpec.getSize(heightMeasureSpec); if (visibleItemsCount != 0) { - int itemWidth = Math.min(AndroidUtilities.dp(70), width / visibleItemsCount); + int itemWidth = Math.min(dp(70), width / visibleItemsCount); if (compressItem.getVisibility() == VISIBLE) { ignoreLayout = true; int compressIconWidth; @@ -6492,7 +6536,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } else { compressIconWidth = 64; } - int padding = Math.max(0, (itemWidth - AndroidUtilities.dp(compressIconWidth)) / 2); + int padding = Math.max(0, (itemWidth - dp(compressIconWidth)) / 2); compressItem.setPadding(padding, 0, padding, 0); ignoreLayout = false; } @@ -6514,11 +6558,11 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { cropItem = new ImageView(parentActivity); cropItem.setScaleType(ImageView.ScaleType.CENTER); - cropItem.setImageResource(R.drawable.msg_photo_crop); + cropItem.setImageResource(R.drawable.media_crop); cropItem.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); itemsLayout.addView(cropItem, LayoutHelper.createLinear(48, 48)); cropItem.setOnClickListener(v -> { - if (captionEditText.getTag() != null) { + if (isCaptionOpen()) { return; } if (isCurrentVideo) { @@ -6548,7 +6592,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mirrorItem = new ImageView(parentActivity); mirrorItem.setScaleType(ImageView.ScaleType.CENTER); - mirrorItem.setImageResource(R.drawable.msg_photo_flip); + mirrorItem.setImageResource(R.drawable.media_flip); mirrorItem.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); itemsLayout.addView(mirrorItem, LayoutHelper.createLinear(48, 48)); mirrorItem.setOnClickListener(v -> cropMirror()); @@ -6556,11 +6600,11 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { paintItem = new ImageView(parentActivity); paintItem.setScaleType(ImageView.ScaleType.CENTER); - paintItem.setImageResource(R.drawable.msg_photo_draw); + paintItem.setImageResource(R.drawable.media_draw); paintItem.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); itemsLayout.addView(paintItem, LayoutHelper.createLinear(48, 48)); paintItem.setOnClickListener(v -> { - if (captionEditText.getTag() != null) { + if (isCaptionOpen()) { return; } if (isCurrentVideo) { @@ -6585,7 +6629,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { muteItem.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); containerView.addView(muteItem, LayoutHelper.createFrame(48, 48, Gravity.LEFT | Gravity.BOTTOM, 16, 0, 0, 0)); muteItem.setOnClickListener(v -> { - if (captionEditText.getTag() != null) { + if (isCaptionOpen()) { return; } muteVideo = !muteVideo; @@ -6601,65 +6645,16 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } }); - cameraItem = new ImageView(parentActivity); - cameraItem.setScaleType(ImageView.ScaleType.CENTER); - cameraItem.setImageResource(R.drawable.photo_add); - cameraItem.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); - cameraItem.setContentDescription(LocaleController.getString("AccDescrTakeMorePics", R.string.AccDescrTakeMorePics)); - containerView.addView(cameraItem, LayoutHelper.createFrame(48, 48, Gravity.RIGHT | Gravity.BOTTOM, 0, 0, 16, 0)); - cameraItem.setOnClickListener(v -> { - if (placeProvider == null || captionEditText.getTag() != null) { - return; - } - placeProvider.needAddMorePhotos(); - closePhoto(true, false); - }); - - tuneItem = new ImageView(parentActivity); - tuneItem.setScaleType(ImageView.ScaleType.CENTER); - tuneItem.setImageResource(R.drawable.msg_photo_settings); - tuneItem.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); - itemsLayout.addView(tuneItem, LayoutHelper.createLinear(48, 48)); - tuneItem.setOnClickListener(v -> { - if (captionEditText.getTag() != null) { - return; - } - if (isCurrentVideo) { - if (!videoConvertSupported) { - return; - } - if (videoTextureView instanceof VideoEditTextureView) { - VideoEditTextureView textureView = (VideoEditTextureView) videoTextureView; - if (textureView.getVideoWidth() <= 0 || textureView.getVideoHeight() <= 0) { - return; - } - } else { - return; - } - } - switchToEditMode(EDIT_MODE_FILTER); - }); - tuneItem.setContentDescription(LocaleController.getString("AccDescrPhotoAdjust", R.string.AccDescrPhotoAdjust)); - - compressItem = new ImageView(parentActivity); + compressItem = new VideoCompressButton(parentActivity); compressItem.setTag(1); - compressItem.setScaleType(ImageView.ScaleType.CENTER); compressItem.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); selectedCompression = selectCompression(); - int compressIconWidth; - if (selectedCompression <= 1) { - compressItem.setImageResource(R.drawable.video_quality1); - } else if (selectedCompression == 2) { - compressItem.setImageResource(R.drawable.video_quality2); - } else { - selectedCompression = compressionsCount - 1; - compressItem.setImageResource(R.drawable.video_quality3); - } + compressItem.setState(videoConvertSupported && compressionsCount > 1, muteVideo, Math.min(resultWidth, resultHeight)); compressItem.setContentDescription(LocaleController.getString("AccDescrVideoQuality", R.string.AccDescrVideoQuality)); itemsLayout.addView(compressItem, LayoutHelper.createLinear(48, 48)); compressItem.setOnClickListener(v -> { - if (captionEditText.getTag() != null || muteVideo) { + if (isCaptionOpen() || muteVideo) { return; } if (compressItem.getTag() == null) { @@ -6676,155 +6671,31 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { requestVideoPreview(1); }); - timeItem = new ImageView(parentActivity); - timeItem.setScaleType(ImageView.ScaleType.CENTER); - timeItem.setImageResource(R.drawable.msg_autodelete); - timeItem.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); - timeItem.setContentDescription(LocaleController.getString("SetTimer", R.string.SetTimer)); - itemsLayout.addView(timeItem, LayoutHelper.createLinear(48, 48)); - timeItem.setOnClickListener(v -> { - if (parentActivity == null || captionEditText.getTag() != null) { + tuneItem = new ImageView(parentActivity); + tuneItem.setScaleType(ImageView.ScaleType.CENTER); + tuneItem.setImageResource(R.drawable.media_settings); + tuneItem.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); + itemsLayout.addView(tuneItem, LayoutHelper.createLinear(48, 48)); + tuneItem.setOnClickListener(v -> { + if (isCaptionOpen()) { return; } - BottomSheet.Builder builder = new BottomSheet.Builder(parentActivity, false, resourcesProvider, 0xff000000); - builder.setUseHardwareLayer(false); - LinearLayout linearLayout = new LinearLayout(parentActivity); - linearLayout.setOrientation(LinearLayout.VERTICAL); - builder.setCustomView(linearLayout); - - TextView titleView = new TextView(parentActivity); - titleView.setLines(1); - titleView.setSingleLine(true); - titleView.setText(LocaleController.getString("MessageLifetime", R.string.MessageLifetime)); - titleView.setTextColor(0xffffffff); - titleView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); - titleView.setEllipsize(TextUtils.TruncateAt.MIDDLE); - titleView.setPadding(AndroidUtilities.dp(21), AndroidUtilities.dp(8), AndroidUtilities.dp(21), AndroidUtilities.dp(4)); - titleView.setGravity(Gravity.CENTER_VERTICAL); - linearLayout.addView(titleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - titleView.setOnTouchListener((v13, event) -> true); - - titleView = new TextView(parentActivity); - titleView.setText(isCurrentVideo ? LocaleController.getString("MessageLifetimeVideo", R.string.MessageLifetimeVideo) : LocaleController.getString("MessageLifetimePhoto", R.string.MessageLifetimePhoto)); - titleView.setTextColor(0xff808080); - titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - titleView.setEllipsize(TextUtils.TruncateAt.MIDDLE); - titleView.setPadding(AndroidUtilities.dp(21), 0, AndroidUtilities.dp(21), AndroidUtilities.dp(8)); - titleView.setGravity(Gravity.CENTER_VERTICAL); - linearLayout.addView(titleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - titleView.setOnTouchListener((v12, event) -> true); - - final BottomSheet bottomSheet = builder.create(); - final NumberPicker numberPicker = new NumberPicker(parentActivity, resourcesProvider); - numberPicker.setMinValue(0); - numberPicker.setMaxValue(28); - Object object = imagesArrLocals.get(currentIndex); - int currentTTL; - if (object instanceof MediaController.PhotoEntry) { - currentTTL = ((MediaController.PhotoEntry) object).ttl; - } else if (object instanceof MediaController.SearchImage) { - currentTTL = ((MediaController.SearchImage) object).ttl; - } else { - currentTTL = 0; - } - if (currentTTL == 0) { - SharedPreferences preferences1 = MessagesController.getGlobalMainSettings(); - numberPicker.setValue(preferences1.getInt("self_destruct", 7)); - } else { - if (currentTTL >= 0 && currentTTL < 21) { - numberPicker.setValue(currentTTL); - } else { - numberPicker.setValue(21 + currentTTL / 5 - 5); - } - } - numberPicker.setTextColor(0xffffffff); - numberPicker.setSelectorColor(0xff4d4d4d); - numberPicker.setFormatter(value -> { - if (value == 0) { - return LocaleController.getString("ShortMessageLifetimeForever", R.string.ShortMessageLifetimeForever); - } else if (value >= 1 && value < 21) { - return LocaleController.formatTTLString(value); - } else { - return LocaleController.formatTTLString((value - 16) * 5); + if (isCurrentVideo) { + if (!videoConvertSupported) { + return; } - }); - linearLayout.addView(numberPicker, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - - FrameLayout buttonsLayout = new FrameLayout(parentActivity) { - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - int count = getChildCount(); - int width = right - left; - for (int a = 0; a < count; a++) { - View child = getChildAt(a); - if ((Integer) child.getTag() == Dialog.BUTTON_POSITIVE) { - child.layout(width - getPaddingRight() - child.getMeasuredWidth(), getPaddingTop(), width - getPaddingRight(), getPaddingTop() + child.getMeasuredHeight()); - } else if ((Integer) child.getTag() == Dialog.BUTTON_NEGATIVE) { - int x = getPaddingLeft(); - child.layout(x, getPaddingTop(), x + child.getMeasuredWidth(), getPaddingTop() + child.getMeasuredHeight()); - } else { - child.layout(getPaddingLeft(), getPaddingTop(), getPaddingLeft() + child.getMeasuredWidth(), getPaddingTop() + child.getMeasuredHeight()); - } + if (videoTextureView instanceof VideoEditTextureView) { + VideoEditTextureView textureView = (VideoEditTextureView) videoTextureView; + if (textureView.getVideoWidth() <= 0 || textureView.getVideoHeight() <= 0) { + return; } - } - }; - buttonsLayout.setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8)); - linearLayout.addView(buttonsLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 52)); - - TextView textView = new TextView(parentActivity); - textView.setMinWidth(AndroidUtilities.dp(64)); - textView.setTag(Dialog.BUTTON_POSITIVE); - textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - textView.setTextColor(getThemedColor(Theme.key_dialogFloatingButton)); - textView.setGravity(Gravity.CENTER); - textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - textView.setText(LocaleController.getString("Done", R.string.Done).toUpperCase()); - textView.setBackgroundDrawable(Theme.getRoundRectSelectorDrawable(0xff49bcf2)); - textView.setPadding(AndroidUtilities.dp(10), 0, AndroidUtilities.dp(10), 0); - buttonsLayout.addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 36, Gravity.TOP | Gravity.RIGHT)); - textView.setOnClickListener(v1 -> { - int value = numberPicker.getValue(); - SharedPreferences preferences1 = MessagesController.getGlobalMainSettings(); - SharedPreferences.Editor editor = preferences1.edit(); - editor.putInt("self_destruct", value); - editor.apply(); - bottomSheet.dismiss(); - int seconds; - if (value >= 0 && value < 21) { - seconds = value; } else { - seconds = (value - 16) * 5; - } - Object object1 = imagesArrLocals.get(currentIndex); - if (object1 instanceof MediaController.PhotoEntry) { - ((MediaController.PhotoEntry) object1).ttl = seconds; - } else if (object1 instanceof MediaController.SearchImage) { - ((MediaController.SearchImage) object1).ttl = seconds; - } - timeItem.setColorFilter(seconds != 0 ? new PorterDuffColorFilter(getThemedColor(Theme.key_dialogFloatingButton), PorterDuff.Mode.SRC_IN) : null); - if (!checkImageView.isChecked()) { - checkImageView.callOnClick(); + return; } - }); - - textView = new TextView(parentActivity); - textView.setMinWidth(AndroidUtilities.dp(64)); - textView.setTag(Dialog.BUTTON_NEGATIVE); - textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - textView.setTextColor(0xffffffff); - textView.setGravity(Gravity.CENTER); - textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - textView.setText(LocaleController.getString("Cancel", R.string.Cancel).toUpperCase()); - textView.setBackgroundDrawable(Theme.getRoundRectSelectorDrawable(0xffffffff)); - textView.setPadding(AndroidUtilities.dp(10), 0, AndroidUtilities.dp(10), 0); - buttonsLayout.addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 36, Gravity.TOP | Gravity.RIGHT)); - textView.setOnClickListener(v14 -> bottomSheet.dismiss()); - bottomSheet.setBackgroundColor(0xff000000); - bottomSheet.show(); - AndroidUtilities.setNavigationBarColor(bottomSheet.getWindow(), 0xff000000, false); - AndroidUtilities.setLightNavigationBar(bottomSheet.getWindow(), false); + } + switchToEditMode(EDIT_MODE_FILTER); }); + tuneItem.setContentDescription(LocaleController.getString("AccDescrPhotoAdjust", R.string.AccDescrPhotoAdjust)); editorDoneLayout = new PickerBottomLayoutViewer(activityContext); editorDoneLayout.setBackgroundColor(0xcc000000); @@ -6878,7 +6749,7 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto resetButton.setTextColor(0xffffffff); resetButton.setGravity(Gravity.CENTER); resetButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_PICKER_SELECTOR_COLOR, 0)); - resetButton.setPadding(AndroidUtilities.dp(20), 0, AndroidUtilities.dp(20), 0); + resetButton.setPadding(dp(20), 0, dp(20), 0); resetButton.setText(LocaleController.getString("Reset", R.string.CropReset).toUpperCase()); resetButton.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); editorDoneLayout.addView(resetButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.CENTER)); @@ -6934,16 +6805,11 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto WindowManager manager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Activity.WINDOW_SERVICE); int rotation = manager.getDefaultDisplay().getRotation(); - checkImageView = new CheckBox(containerView.getContext(), R.drawable.selectphoto_large) { - @Override - public boolean onTouchEvent(MotionEvent event) { - return bottomTouchEnabled && super.onTouchEvent(event); - } - }; + checkImageView = new CheckBox(containerView.getContext(), R.drawable.selectphoto_large); checkImageView.setDrawBackground(true); checkImageView.setHasBorder(true); checkImageView.setSize(34); - checkImageView.setCheckOffset(AndroidUtilities.dp(1)); + checkImageView.setCheckOffset(dp(1)); checkImageView.setColor(getThemedColor(Theme.key_dialogFloatingButton), 0xffffffff); checkImageView.setVisibility(View.GONE); containerView.addView(checkImageView, LayoutHelper.createFrame(34, 34, Gravity.RIGHT | Gravity.TOP, 0, rotation == Surface.ROTATION_270 || rotation == Surface.ROTATION_90 ? 61 : 71, 11, 0)); @@ -6951,7 +6817,7 @@ public boolean onTouchEvent(MotionEvent event) { ((FrameLayout.LayoutParams) checkImageView.getLayoutParams()).topMargin += AndroidUtilities.statusBarHeight; } checkImageView.setOnClickListener(v -> { - if (captionEditText.getTag() != null) { + if (isCaptionOpen()) { return; } setPhotoChecked(); @@ -6963,7 +6829,7 @@ public boolean onTouchEvent(MotionEvent event) { ((FrameLayout.LayoutParams) photosCounterView.getLayoutParams()).topMargin += AndroidUtilities.statusBarHeight; } photosCounterView.setOnClickListener(v -> { - if (captionEditText.getTag() != null || placeProvider == null || placeProvider.getSelectedPhotosOrder() == null || placeProvider.getSelectedPhotosOrder().isEmpty()) { + if (isCaptionOpen() || placeProvider == null || placeProvider.getSelectedPhotosOrder() == null || placeProvider.getSelectedPhotosOrder().isEmpty()) { return; } togglePhotosListView(!isPhotosListViewVisible, true); @@ -7003,396 +6869,50 @@ protected int calculateTimeForDeceleration(int dx) { ignoreDidSetImage = false; }); - captionEditText = new PhotoViewerCaptionEnterView(this, activityContext, containerView, windowView, resourcesProvider) { - @Override - public boolean dispatchTouchEvent(MotionEvent ev) { - try { - return !bottomTouchEnabled && super.dispatchTouchEvent(ev); - } catch (Exception e) { - FileLog.e(e); - } - return false; - } + hintView = new UndoView(activityContext, null, false, resourcesProvider); + hintView.setAdditionalTranslationY(dp(112)); + hintView.setColors(0xf9222222, 0xffffffff); + containerView.addView(hintView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.LEFT, 8, 0, 8, 8)); - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - try { - return !bottomTouchEnabled && super.onInterceptTouchEvent(ev); - } catch (Exception e) { - FileLog.e(e); - } - return false; - } + if (AndroidUtilities.isAccessibilityScreenReaderEnabled()) { + playButtonAccessibilityOverlay = new View(activityContext); + playButtonAccessibilityOverlay.setContentDescription(LocaleController.getString("AccActionPlay", R.string.AccActionPlay)); + playButtonAccessibilityOverlay.setFocusable(true); + containerView.addView(playButtonAccessibilityOverlay, LayoutHelper.createFrame(64, 64, Gravity.CENTER)); + } - @Override - public boolean onTouchEvent(MotionEvent event) { - if (bottomTouchEnabled && event.getAction() == MotionEvent.ACTION_DOWN) { - keyboardAnimationEnabled = true; - } - return !bottomTouchEnabled && super.onTouchEvent(event); - } + doneButtonFullWidth.setBackground(Theme.AdaptiveRipple.filledRect(getThemedColor(Theme.key_featuredStickers_addButton), 6)); + doneButtonFullWidth.setTextColor(getThemedColor(Theme.key_featuredStickers_buttonText)); - @Override - protected void extendActionMode(ActionMode actionMode, Menu menu) { - if (parentChatActivity != null) { - parentChatActivity.extendActionMode(menu); - } + textSelectionHelper.allowScrollPrentRelative = true; + textSelectionHelper.useMovingOffset = false; + View overlay = textSelectionHelper.getOverlayView(windowView.getContext()); + if (overlay != null) { + AndroidUtilities.removeFromParent(overlay); + containerView.addView(overlay); + } + textSelectionHelper.setParentView(containerView); + textSelectionHelper.setInvalidateParent(); + } + + private Bulletin limitBulletin; + public boolean showCaptionLimitBulletin(FrameLayout view) { + if (!(parentFragment instanceof ChatActivity) || !ChatObject.isChannelAndNotMegaGroup(((ChatActivity) parentFragment).getCurrentChat())) { + return false; + } + limitBulletin = BulletinFactory.of(view, resourcesProvider).createCaptionLimitBulletin(MessagesController.getInstance(currentAccount).captionLengthLimitPremium, ()->{ + closePhoto(false, false); + if (parentAlert != null) { + parentAlert.dismiss(true); } - }; - captionEditText.setDelegate(new PhotoViewerCaptionEnterView.PhotoViewerCaptionEnterViewDelegate() { - @Override - public void onCaptionEnter() { - closeCaptionEnter(true); + if (parentFragment != null) { + parentFragment.presentFragment(new PremiumPreviewFragment("caption_limit")); } - - @Override - public void onTextChanged(CharSequence text) { - if (mentionsAdapter != null && captionEditText != null && parentChatActivity != null && text != null) { - mentionsAdapter.searchUsernameOrHashtag(text.toString(), captionEditText.getCursorPosition(), parentChatActivity.messages, false, false); - } - int color = getThemedColor(Theme.key_dialogFloatingIcon); - if (captionEditText.getCaptionLimitOffset() < 0) { - captionLimitView.setText(Integer.toString(captionEditText.getCaptionLimitOffset())); - captionLimitView.setVisibility(pickerViewSendButton.getVisibility()); - pickerViewSendButton.setColorFilter(new PorterDuffColorFilter(ColorUtils.setAlphaComponent(color, (int) (Color.alpha(color) * 0.58f)), PorterDuff.Mode.MULTIPLY)); - } else { - pickerViewSendButton.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)); - captionLimitView.setVisibility(View.GONE); - } - if (placeProvider != null) { - placeProvider.onCaptionChanged(text); - } - } - - @Override - public void onWindowSizeChanged(int size) { - int height = AndroidUtilities.dp(36 * Math.min(3, mentionsAdapter.getItemCount()) + (mentionsAdapter.getItemCount() > 3 ? 18 : 0)); - if (size - ActionBar.getCurrentActionBarHeight() * 2 < height) { - allowMentions = false; - if (mentionListView != null && mentionListView.getVisibility() == View.VISIBLE) { - mentionListView.setVisibility(View.INVISIBLE); - } - } else { - allowMentions = true; - if (mentionListView != null && mentionListView.getVisibility() == View.INVISIBLE) { - mentionListView.setVisibility(View.VISIBLE); - } - } - } - - @Override - public void onEmojiViewOpen() { - navigationBar.setVisibility(View.INVISIBLE); - animateNavBarColorTo(Theme.getColor(Theme.key_chat_emojiPanelBackground, captionEditText.getResourcesProvider()), false); - } - - @Override - public void onEmojiViewCloseStart() { - navigationBar.setVisibility(currentEditMode != EDIT_MODE_FILTER ? View.VISIBLE : View.INVISIBLE); - animateNavBarColorTo(0xff000000); - setOffset(captionEditText.getEmojiPadding()); - if (captionEditText.getTag() != null) { - if (isCurrentVideo) { - actionBarContainer.setTitleAnimated(muteVideo ? LocaleController.getString("GifCaption", R.string.GifCaption) : LocaleController.getString("VideoCaption", R.string.VideoCaption),true, false); - } else { - actionBarContainer.setTitleAnimated(LocaleController.getString("PhotoCaption", R.string.PhotoCaption), true, false); - } - checkImageView.animate().alpha(0f).setDuration(220).start(); - photosCounterView.animate().alpha(0f).setDuration(220).start(); - selectedPhotosListView.animate().alpha(0.0f).translationY(-AndroidUtilities.dp(10)).setDuration(220).start(); - } else { - checkImageView.animate().alpha(1f).setDuration(220).start(); - photosCounterView.animate().alpha(1f).setDuration(220).start(); - if (lastTitle != null) { - actionBarContainer.setTitleAnimated(lastTitle, true, false); - lastTitle = null; - } - } - } - - @Override - public void onEmojiViewCloseEnd() { - setOffset(0); - captionEditText.setVisibility(View.GONE); - } - - 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) { - child.setTranslationY(offset); - } - } - } - }); - if (Build.VERSION.SDK_INT >= 19) { - captionEditText.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); - } - captionEditText.setVisibility(View.GONE); - containerView.addView(captionEditText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.LEFT)); - - mentionListView = new RecyclerListView(activityContext, resourcesProvider) { - @Override - public boolean dispatchTouchEvent(MotionEvent ev) { - return !bottomTouchEnabled && super.dispatchTouchEvent(ev); - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - return !bottomTouchEnabled && super.onInterceptTouchEvent(ev); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - return !bottomTouchEnabled && super.onTouchEvent(event); - } - - @Override - public void setTranslationY(float translationY) { - super.setTranslationY(translationY); - invalidate(); - - if (getParent() != null) { - ((View) getParent()).invalidate(); - } - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - - if (mentionListViewVisible && getVisibility() == VISIBLE && mentionListAnimation == null) { - setTranslationY(h - oldh); - mentionListView.setTranslationY(MathUtils.clamp(mentionListView.getTranslationY(), Math.min(h - oldh, 0), Math.max(h - oldh, 0))); - mentionListAnimation = new SpringAnimation(this, DynamicAnimation.TRANSLATION_Y) - .setMinValue(Math.min(h - oldh, 0)) - .setMaxValue(Math.max(h - oldh, 0)) - .setSpring(new SpringForce(0) - .setStiffness(750f) - .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)); - mentionListAnimation.addEndListener((animation, canceled, value, velocity) -> { - if (mentionListAnimation == animation) { - mentionListAnimation = null; - } - }); - mentionListAnimation.start(); - } - } - }; - mentionListView.setTag(5); - mentionLayoutManager = new LinearLayoutManager(activityContext) { - @Override - public boolean supportsPredictiveItemAnimations() { - return false; - } - }; - mentionLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); - mentionListView.setLayoutManager(mentionLayoutManager); - mentionListView.setVisibility(View.GONE); - mentionListView.setClipToPadding(true); - mentionListView.setOverScrollMode(RecyclerListView.OVER_SCROLL_NEVER); - containerView.addView(mentionListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 110, Gravity.LEFT | Gravity.BOTTOM)); - - mentionListView.setAdapter(mentionsAdapter = new MentionsAdapter(activityContext, true, 0, 0, new MentionsAdapter.MentionsAdapterDelegate() { - @Override - public void onItemCountUpdate(int oldCount, int newCount) { - - } - - @Override - public void needChangePanelVisibility(boolean show) { - if (show) { - FrameLayout.LayoutParams layoutParams3 = (FrameLayout.LayoutParams) mentionListView.getLayoutParams(); - int height = 36 * Math.min(3, mentionsAdapter.getItemCount()) + (mentionsAdapter.getItemCount() > 3 ? 18 : 0); - layoutParams3.height = AndroidUtilities.dp(height); - layoutParams3.topMargin = -AndroidUtilities.dp(height); - mentionListView.setLayoutParams(layoutParams3); - - if (mentionListAnimation != null) { - mentionListAnimation.cancel(); - mentionListAnimation = null; - } - - if (mentionListView.getVisibility() == View.VISIBLE) { - mentionListView.setTranslationY(0); - return; - } else { - mentionLayoutManager.scrollToPositionWithOffset(0, 10000); - } - if (allowMentions) { - mentionListView.setVisibility(View.VISIBLE); - mentionListViewVisible = true; - mentionListView.setTranslationY(AndroidUtilities.dp(height)); - mentionListView.setTranslationY(MathUtils.clamp(mentionListView.getTranslationY(), 0, AndroidUtilities.dp(height))); - mentionListAnimation = new SpringAnimation(mentionListView, DynamicAnimation.TRANSLATION_Y) - .setMinValue(0) - .setMaxValue(AndroidUtilities.dp(height)) - .setSpring(new SpringForce(0f) - .setStiffness(750f) - .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)); - mentionListAnimation.addEndListener((animation, canceled, value, velocity) -> { - if (mentionListAnimation == animation) { - mentionListAnimation = null; - } - }); - mentionListAnimation.start(); - } else { - mentionListView.setTranslationY(0); - mentionListView.setVisibility(View.INVISIBLE); - } - } else { - if (mentionListAnimation != null) { - mentionListAnimation.cancel(); - mentionListAnimation = null; - } - - if (mentionListView.getVisibility() == View.GONE) { - return; - } - if (allowMentions) { - mentionListViewVisible = false; - mentionListView.setTranslationY(MathUtils.clamp(mentionListView.getTranslationY(), 0, mentionListView.getMeasuredHeight())); - mentionListAnimation = new SpringAnimation(mentionListView, DynamicAnimation.TRANSLATION_Y) - .setMinValue(0) - .setMaxValue(mentionListView.getMeasuredHeight()) - .setSpring(new SpringForce(mentionListView.getMeasuredHeight()) - .setStiffness(750f) - .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)); - mentionListAnimation.addEndListener((animation, canceled, value, velocity) -> { - if (mentionListAnimation == animation) { - mentionListView.setVisibility(View.GONE); - mentionListAnimation = null; - } - }); - mentionListAnimation.start(); - } else { - mentionListView.setVisibility(View.GONE); - } - } - } - - @Override - public void onContextSearch(boolean searching) { - - } - - @Override - public void onContextClick(TLRPC.BotInlineResult result) { - - } - }, resourcesProvider)); - - mentionListView.setOnItemClickListener((view, position) -> { - Object object = mentionsAdapter.getItem(position); - int start = mentionsAdapter.getResultStartPosition(); - int len = mentionsAdapter.getResultLength(); - if (object instanceof TLRPC.User) { - TLRPC.User user = (TLRPC.User) object; - String username = UserObject.getPublicUsername(user); - if (username != null) { - captionEditText.replaceWithText(start, len, "@" + username + " ", false); - } else { - String name = UserObject.getFirstName(user); - Spannable spannable = new SpannableString(name + " "); - spannable.setSpan(new URLSpanUserMentionPhotoViewer("" + user.id, true), 0, spannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - captionEditText.replaceWithText(start, len, spannable, false); - } - } else if (object instanceof String) { - captionEditText.replaceWithText(start, len, object + " ", false); - } else if (object instanceof MediaDataController.KeywordResult) { - String code = ((MediaDataController.KeywordResult) object).emoji; - captionEditText.addEmojiToRecent(code); - if (code != null && code.startsWith("animated_")) { - try { - long documentId = Long.parseLong(code.substring(9)); - TLRPC.Document document = AnimatedEmojiDrawable.findDocument(currentAccount, documentId); - SpannableString emoji = new SpannableString(MessageObject.findAnimatedEmojiEmoticon(document)); - AnimatedEmojiSpan span; - if (document != null) { - span = new AnimatedEmojiSpan(document, captionEditText.getMessageEditText().getPaint().getFontMetricsInt()); - } else { - span = new AnimatedEmojiSpan(documentId, captionEditText.getMessageEditText().getPaint().getFontMetricsInt()); - } - emoji.setSpan(span, 0, emoji.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - captionEditText.replaceWithText(start, len, emoji, false); - } catch (Exception ignore) { - captionEditText.replaceWithText(start, len, code, true); - } - } else { - captionEditText.replaceWithText(start, len, code, true); - } - } - }); - - mentionListView.setOnItemLongClickListener((view, position) -> { - Object object = mentionsAdapter.getItem(position); - int start = mentionsAdapter.getResultStartPosition(); - int len = mentionsAdapter.getResultLength(); - if (object instanceof String) { - AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity, resourcesProvider); - builder.setTitle(LocaleController.getString("NekoX", R.string.NekoX)); - builder.setMessage(LocaleController.getString("ClearSearch", R.string.ClearSearch)); - builder.setPositiveButton(LocaleController.getString("ClearButton", R.string.ClearButton), (dialogInterface, i) -> mentionsAdapter.clearRecentHashtags()); - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); - return true; - } else if (object instanceof TLRPC.User) { - TLRPC.User user = (TLRPC.User) object; - String name = UserObject.getFirstName(user); - Spannable spannable = new SpannableString("@" + name + " "); - spannable.setSpan(new URLSpanUserMentionPhotoViewer("" + user.id, true), 0, spannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - captionEditText.replaceWithText(start, len, spannable, false); - return true; - } - return false; - }); - - hintView = new UndoView(activityContext, null, false, resourcesProvider); - hintView.setAdditionalTranslationY(AndroidUtilities.dp(112)); - hintView.setColors(0xf9222222, 0xffffffff); - containerView.addView(hintView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.LEFT, 8, 0, 8, 8)); - - if (AndroidUtilities.isAccessibilityScreenReaderEnabled()) { - playButtonAccessibilityOverlay = new View(activityContext); - playButtonAccessibilityOverlay.setContentDescription(LocaleController.getString("AccActionPlay", R.string.AccActionPlay)); - playButtonAccessibilityOverlay.setFocusable(true); - containerView.addView(playButtonAccessibilityOverlay, LayoutHelper.createFrame(64, 64, Gravity.CENTER)); - } - - doneButtonFullWidth.setBackground(Theme.AdaptiveRipple.filledRect(getThemedColor(Theme.key_featuredStickers_addButton), 6)); - doneButtonFullWidth.setTextColor(getThemedColor(Theme.key_featuredStickers_buttonText)); - - textSelectionHelper = new TextSelectionHelper.SimpleTextSelectionHelper(null, new DarkThemeResourceProvider()) { - @Override - public int getParentBottomPadding() { - return 0;//AndroidUtilities.dp(80); - } - }; - textSelectionHelper.allowScrollPrentRelative = true; - 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) { - if (!(parentFragment instanceof ChatActivity) || !ChatObject.isChannelAndNotMegaGroup(((ChatActivity) parentFragment).getCurrentChat())) { - return; - } - BulletinFactory.of(view, resourcesProvider).createCaptionLimitBulletin(MessagesController.getInstance(currentAccount).captionLengthLimitPremium, ()->{ - closePhoto(false, false); - if (parentAlert != null) { - parentAlert.dismiss(true); - } - if (parentFragment != null) { - parentFragment.presentFragment(new PremiumPreviewFragment("caption_limit")); - } - }).show(); - } + }).setOnHideListener(() -> { + limitBulletin = null; + }).show(); + return true; + } public ChatAttachAlert getParentAlert() { return parentAlert; @@ -7446,12 +6966,12 @@ private void showScheduleDatePickerDialog() { private void translateComment(Locale target) { - String origin = captionEditText.getFieldCharSequence().toString(); + String origin = captionEdit.getText().toString(); TranslateDb db = TranslateDb.forLocale(target); if (db.contains(origin)) { String translated = db.query(origin); - captionEditText.setFieldText(translated); + captionEdit.setText(translated); return; } @@ -7474,7 +6994,7 @@ private void translateComment(Locale target) { @Override public void onSuccess(@NotNull String translation) { status.dismiss(); - captionEditText.setFieldText(translation); + captionEdit.setText(translation); } @Override @@ -7501,7 +7021,7 @@ private void replacePressed() { } private void sendPressed(boolean notify, int scheduleDate, boolean replace, boolean forceDocument, boolean confirmed) { - if (captionEditText.getTag() != null) { + if (isCaptionOpen()) { return; } if (placeProvider != null && !doneButtonPressed) { @@ -7514,7 +7034,7 @@ private void sendPressed(boolean notify, int scheduleDate, boolean replace, bool dialog_id = ((TLRPC.User) setAvatarFor.object).id; } AlertDialog.Builder builder = new AlertDialog.Builder(containerView.getContext()); - builder.setAdditionalHorizontalPadding(AndroidUtilities.dp(8)); + builder.setAdditionalHorizontalPadding(dp(8)); SuggestUserPhotoView suggestUserPhotoView = new SuggestUserPhotoView(containerView.getContext()); suggestUserPhotoView.setImages(setAvatarFor.object, containerView, photoCropView); builder.setTopView(suggestUserPhotoView); @@ -7623,7 +7143,7 @@ public void onDismiss(DialogInterface dialog) { long sizeToCheck = (long) (videoEditedInfo.estimatedSize * 0.9f); if ((sizeToCheck > FileLoader.DEFAULT_MAX_FILE_SIZE && !UserConfig.getInstance(currentAccount).isPremium()) || sizeToCheck > FileLoader.DEFAULT_MAX_FILE_SIZE_PREMIUM) { if (parentAlert != null) { - LimitReachedBottomSheet limitReachedBottomSheet = new LimitReachedBottomSheet(parentAlert.getBaseFragment(), parentAlert.getContainer().getContext(), LimitReachedBottomSheet.TYPE_LARGE_FILE, UserConfig.selectedAccount); + LimitReachedBottomSheet limitReachedBottomSheet = new LimitReachedBottomSheet(parentAlert.getBaseFragment(), parentAlert.getContainer().getContext(), LimitReachedBottomSheet.TYPE_LARGE_FILE, UserConfig.selectedAccount, null); limitReachedBottomSheet.show(); } return; @@ -7699,6 +7219,13 @@ private void showForward(ArrayList fmessages, boolean noQuote) { ((LaunchActivity) parentActivity).presentFragment(fragment, false, true); } + private boolean isCaptionOpen() { + return ( + captionEdit != null && + (captionEdit.keyboardNotifier.keyboardVisible() || captionEdit.editText.isPopupShowing()) + ); + } + private void showShareAlert(ArrayList messages) { final FrameLayout photoContainerView = containerView; requestAdjustToNothing(); @@ -7759,10 +7286,10 @@ private void updateActionBarTitlePadding() { } } if (checkImageView != null && checkImageView.getVisibility() == View.VISIBLE) { - width = Math.max(width, AndroidUtilities.dp(48)); + width = Math.max(width, dp(48)); } if (photosCounterView != null && photosCounterView.getVisibility() == View.VISIBLE) { - width = Math.max(width, AndroidUtilities.dp(100)); + width = Math.max(width, dp(100)); } actionBarContainer.updateRightPadding(width, false); } @@ -7846,9 +7373,27 @@ public void onAnimationCancel(Animator animation) { }).start(); } - private class CaptionTextView extends SpoilersTextView { - public CaptionTextView(Context context) { + public static class CaptionTextView extends SpoilersTextView { + + private final PhotoViewer.CaptionScrollView scrollView; + private final TextSelectionHelper.SimpleTextSelectionHelper textSelectionHelper; + private LinkSpanDrawable pressedLink; + private final LinkSpanDrawable.LinkCollector links = new LinkSpanDrawable.LinkCollector(this); + private final Utilities.Callback3 onLinkLongPress; + private final Utilities.Callback2 onLinkClick; + + public CaptionTextView( + Context context, + PhotoViewer.CaptionScrollView scrollView, + TextSelectionHelper.SimpleTextSelectionHelper textSelectionHelper, + Utilities.Callback2 onLinkClick, + Utilities.Callback3 onLinkLongPress + ) { super(context); + this.scrollView = scrollView; + this.onLinkClick = onLinkClick; + this.onLinkLongPress = onLinkLongPress; + this.textSelectionHelper = textSelectionHelper; ViewHelper.setPadding(this, 16, 8, 16, 8); setLinkTextColor(0xff79c4fc); setTextColor(0xffffffff); @@ -7856,17 +7401,12 @@ public CaptionTextView(Context context) { setGravity(Gravity.CENTER_VERTICAL | LayoutHelper.getAbsoluteGravityStart()); setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); setOnClickListener((v) -> { - if (!needCaptionLayout) { - captionScrollView.smoothScrollBy(0, AndroidUtilities.dp(64)); - return; + if (scrollView != null) { + scrollView.smoothScrollBy(0, dp(64)); } - openCaptionEnter(); }); } - private LinkSpanDrawable pressedLink; - private LinkSpanDrawable.LinkCollector links = new LinkSpanDrawable.LinkCollector(this); - @Override public boolean onTouchEvent(MotionEvent event) { if (getLayout() == null) { @@ -7874,7 +7414,7 @@ public boolean onTouchEvent(MotionEvent event) { } if (textSelectionHelper != null && getStaticTextLayout() != null) { textSelectionHelper.setSelectabeleView(this); - textSelectionHelper.setScrollingParent(captionScrollView); + textSelectionHelper.setScrollingParent(scrollView); textSelectionHelper.update(getPaddingLeft(), getPaddingTop()); textSelectionHelper.onTouchEvent(event); } @@ -7907,7 +7447,7 @@ public boolean onTouchEvent(MotionEvent event) { final LinkSpanDrawable savedPressedLink = pressedLink; postDelayed(() -> { if (savedPressedLink == pressedLink && pressedLink != null && pressedLink.getSpan() instanceof URLSpan) { - onLinkLongPress((URLSpan) pressedLink.getSpan(), this, () -> links.clear()); + onLinkLongPress.run((URLSpan) pressedLink.getSpan(), this, links::clear); pressedLink = null; } }, ViewConfiguration.getLongPressTimeout()); @@ -7917,7 +7457,7 @@ public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP) { links.clear(); if (pressedLink != null && pressedLink.getSpan() == touchLink) { - onLinkClick(pressedLink.getSpan(), this); + onLinkClick.run(pressedLink.getSpan(), this); } pressedLink = null; linkResult = true; @@ -7927,9 +7467,7 @@ public boolean onTouchEvent(MotionEvent event) { pressedLink = null; linkResult = true; } - - boolean b = linkResult || super.onTouchEvent(event); - return bottomTouchEnabled && b; + return linkResult || super.onTouchEvent(event); } @Override @@ -8038,8 +7576,8 @@ private void checkLoadingPath() { loadingPath.rewind(); } if (layout != null) { - final float horizontalPadding = AndroidUtilities.dp(16); - final float verticalPadding = AndroidUtilities.dp(8); + final float horizontalPadding = dp(16); + final float verticalPadding = dp(8); float t = 0; for (int i = 0; i < layout.getLineCount(); ++i) { float l = layout.getLineLeft(i) - horizontalPadding / 3f; @@ -8065,18 +7603,12 @@ protected boolean verifyDrawable(@NonNull Drawable who) { } private int getLeftInset() { - if (lastInsets != null && Build.VERSION.SDK_INT >= 21) { - return ((WindowInsets) lastInsets).getSystemWindowInsetLeft(); - } - return 0; + return insets.left; } - private int getRightInset() { - if (lastInsets != null && Build.VERSION.SDK_INT >= 21) { - return ((WindowInsets) lastInsets).getSystemWindowInsetRight(); - } - return 0; - } + private int getRightInset() { + return insets.right; + } private void dismissInternal() { try { @@ -8114,10 +7646,16 @@ private void switchToPip(boolean fromGesture) { Bitmap bitmap = animation.getAnimatedBitmap(); if (bitmap != null) { try { - Bitmap src = videoTextureView.getBitmap(bitmap.getWidth(), bitmap.getHeight()); - Canvas canvas = new Canvas(bitmap); - canvas.drawBitmap(src, 0, 0, null); - src.recycle(); + if (usedSurfaceView) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + AndroidUtilities.getBitmapFromSurface(videoSurfaceView, bitmap); + } + } else { + Bitmap src = videoTextureView.getBitmap(bitmap.getWidth(), bitmap.getHeight()); + Canvas canvas = new Canvas(bitmap); + canvas.drawBitmap(src, 0, 0, null); + src.recycle(); + } } catch (Throwable e) { FileLog.e(e); } @@ -8144,21 +7682,40 @@ public void onAnimationEnd(Animator animation) { if (Build.VERSION.SDK_INT >= 21 && PipVideoOverlay.IS_TRANSITION_ANIMATION_SUPPORTED) { pipAnimationInProgress = true; org.telegram.ui.Components.Rect rect = PipVideoOverlay.getPipRect(true, aspectRatioFrameLayout.getAspectRatio()); - - float scale = rect.width / videoTextureView.getWidth(); + View textureView = usedSurfaceView ? videoSurfaceView : videoTextureView; + float scale = rect.width / textureView.getWidth(); ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1f); - float fromX = videoTextureView.getTranslationX(); - float fromY = videoTextureView.getTranslationY() + translationY; - float fromY2 = textureImageView.getTranslationY() + translationY; + float fromX, fromY, fromY2; + if (usedSurfaceView) { + fromX = aspectRatioFrameLayout.getLeft() + textureView.getTranslationX(); + fromY = aspectRatioFrameLayout.getTop() + textureView.getTranslationY() + translationY; + fromY2 = aspectRatioFrameLayout.getTop() + textureImageView.getTranslationY() + translationY; + } else { + fromX = textureView.getTranslationX(); + fromY = textureView.getTranslationY() + translationY; + fromY2 = textureImageView.getTranslationY() + translationY; + } float toX = rect.x; float toX2 = rect.x - aspectRatioFrameLayout.getX() + getLeftInset(); float toY = rect.y; float toY2 = rect.y - aspectRatioFrameLayout.getY(); + if (videoSurfaceView != null) { + videoPlayer.player.pause(); + textureImageView.setVisibility(View.VISIBLE); + if (usedSurfaceView) { + Bitmap bitmap = Bitmaps.createBitmap(videoSurfaceView.getWidth(), videoSurfaceView.getHeight(), Bitmap.Config.ARGB_8888); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + AndroidUtilities.getBitmapFromSurface(videoSurfaceView, bitmap); + } + textureImageView.setImageBitmap(bitmap); + } + videoSurfaceView.setVisibility(View.INVISIBLE); + } textureImageView.setTranslationY(fromY2); - videoTextureView.setTranslationY(fromY); + textureView.setTranslationY(fromY); if (firstFrameView != null) { firstFrameView.setTranslationY(fromY); } @@ -8179,11 +7736,11 @@ public void onAnimationEnd(Animator animation) { ViewOutlineProvider outlineProvider = new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { - outline.setRoundRect(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight(), (float) valueAnimator.getAnimatedValue() * AndroidUtilities.dp(PipVideoOverlay.ROUNDED_CORNERS_DP) * (1f / scale)); + outline.setRoundRect(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight(), (float) valueAnimator.getAnimatedValue() * dp(PipVideoOverlay.ROUNDED_CORNERS_DP) * (1f / scale)); } }; - videoTextureView.setOutlineProvider(outlineProvider); - videoTextureView.setClipToOutline(true); + textureView.setOutlineProvider(outlineProvider); + textureView.setClipToOutline(true); textureImageView.setOutlineProvider(outlineProvider); textureImageView.setClipToOutline(true); if (firstFrameView != null) { @@ -8200,18 +7757,16 @@ public void getOutline(View view, Outline outline) { textureImageView.invalidateOutline(); } - if (videoTextureView != null) { - videoTextureView.setTranslationX(fromX * (1f - xValue) + (toX2) * xValue); - videoTextureView.setTranslationY(fromY * (1f - yValue) + (toY2) * yValue); - videoTextureView.invalidateOutline(); + textureView.setTranslationX(fromX * (1f - xValue) + (toX2) * xValue); + textureView.setTranslationY(fromY * (1f - yValue) + (toY2) * yValue); + textureView.invalidateOutline(); - if (firstFrameView != null) { - firstFrameView.setTranslationX(videoTextureView.getTranslationX()); - firstFrameView.setTranslationY(videoTextureView.getTranslationY()); - firstFrameView.setScaleX(videoTextureView.getScaleX()); - firstFrameView.setScaleY(videoTextureView.getScaleY()); - firstFrameView.invalidateOutline(); - } + if (firstFrameView != null) { + firstFrameView.setTranslationX(textureView.getTranslationX()); + firstFrameView.setTranslationY(textureView.getTranslationY()); + firstFrameView.setScaleX(textureView.getScaleX()); + firstFrameView.setScaleY(textureView.getScaleY()); + firstFrameView.invalidateOutline(); } }); @@ -8222,8 +7777,8 @@ public void getOutline(View view, Outline outline) { progressAnimator, ObjectAnimator.ofFloat(textureImageView, View.SCALE_X, scale), ObjectAnimator.ofFloat(textureImageView, View.SCALE_Y, scale), - ObjectAnimator.ofFloat(videoTextureView, View.SCALE_X, scale), - ObjectAnimator.ofFloat(videoTextureView, View.SCALE_Y, scale), + ObjectAnimator.ofFloat(textureView, View.SCALE_X, scale), + ObjectAnimator.ofFloat(textureView, View.SCALE_Y, scale), ObjectAnimator.ofInt(backgroundDrawable, AnimationProperties.COLOR_DRAWABLE_ALPHA, 0), valueAnimator ); @@ -8239,10 +7794,8 @@ public void getOutline(View view, Outline outline) { public void onAnimationEnd(Animator animation) { pipAnimationInProgress = false; switchToInlineRunnable.run(); - AndroidUtilities.runOnUIThread(()->{ - if (videoTextureView != null) { - videoTextureView.setOutlineProvider(null); - } + AndroidUtilities.runOnUIThread(() -> { + textureView.setOutlineProvider(null); if (textureImageView != null) { textureImageView.setOutlineProvider(null); } @@ -8405,12 +7958,11 @@ public void exitFromPip() { isInline = false; - if (photoViewerWebView == null && videoTextureView != null) { - if (videoTextureView.getParent() != null) { - ((ViewGroup) videoTextureView.getParent()).removeView(videoTextureView); - } - videoTextureView.setVisibility(View.INVISIBLE); - aspectRatioFrameLayout.addView(videoTextureView); + View textureView = usedSurfaceView ? videoSurfaceView : videoTextureView; + if (photoViewerWebView == null && textureView != null) { + AndroidUtilities.removeFromParent(textureView); + textureView.setVisibility(View.INVISIBLE); + aspectRatioFrameLayout.addView(textureView); } if (ApplicationLoader.mainInterfacePaused) { @@ -8422,7 +7974,7 @@ public void exitFromPip() { } if (photoViewerWebView == null) { - if (Build.VERSION.SDK_INT >= 21 && videoTextureView != null && PipVideoOverlay.IS_TRANSITION_ANIMATION_SUPPORTED) { + if (Build.VERSION.SDK_INT >= 21 && textureView != null && PipVideoOverlay.IS_TRANSITION_ANIMATION_SUPPORTED) { pipAnimationInProgress = true; org.telegram.ui.Components.Rect rect = PipVideoOverlay.getPipRect(false, aspectRatioFrameLayout.getAspectRatio()); @@ -8431,26 +7983,26 @@ public void exitFromPip() { textureImageView.setScaleY(scale); textureImageView.setTranslationX(rect.x); textureImageView.setTranslationY(rect.y); - videoTextureView.setScaleX(scale); - videoTextureView.setScaleY(scale); - videoTextureView.setTranslationX(rect.x - aspectRatioFrameLayout.getX()); - videoTextureView.setTranslationY(rect.y - aspectRatioFrameLayout.getY()); + textureView.setScaleX(scale); + textureView.setScaleY(scale); + textureView.setTranslationX(rect.x - aspectRatioFrameLayout.getX()); + textureView.setTranslationY(rect.y - aspectRatioFrameLayout.getY()); if (firstFrameView != null) { firstFrameView.setScaleX(scale); firstFrameView.setScaleY(scale); - firstFrameView.setTranslationX(videoTextureView.getTranslationX()); - firstFrameView.setTranslationY(videoTextureView.getTranslationY()); + firstFrameView.setTranslationX(textureView.getTranslationX()); + firstFrameView.setTranslationY(textureView.getTranslationY()); } inlineOutAnimationProgress = 0f; ViewOutlineProvider outlineProvider = new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { - outline.setRoundRect(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight(), (1f - inlineOutAnimationProgress) * AndroidUtilities.dp(PipVideoOverlay.ROUNDED_CORNERS_DP) * (1f / scale)); + outline.setRoundRect(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight(), (1f - inlineOutAnimationProgress) * dp(PipVideoOverlay.ROUNDED_CORNERS_DP) * (1f / scale)); } }; - videoTextureView.setOutlineProvider(outlineProvider); - videoTextureView.setClipToOutline(true); + textureView.setOutlineProvider(outlineProvider); + textureView.setClipToOutline(true); textureImageView.setOutlineProvider(outlineProvider); textureImageView.setClipToOutline(true); if (firstFrameView != null) { @@ -8477,8 +8029,24 @@ public void getOutline(View view, Outline outline) { FileLog.e(e); } - if (Build.VERSION.SDK_INT >= 21) { - waitingForDraw = 4; + if (usedSurfaceView) { + videoPlayer.player.setVideoTextureView(null); + videoPlayer.setSurfaceView(videoSurfaceView); + videoSurfaceView.setVisibility(View.INVISIBLE); + waitingForFirstTextureUpload = 2; + changingTextureView = false; + containerView.invalidate(); +// checkChangedTextureView(false); +// AndroidUtilities.runOnUIThread(() -> { +// PipVideoOverlay.dismiss(true, true); +// }, 100); + if (Build.VERSION.SDK_INT >= 21) { + waitingForDraw = 4; + } + } else { + if (Build.VERSION.SDK_INT >= 21) { + waitingForDraw = 4; + } } } @@ -8538,9 +8106,9 @@ private void onUserLeaveHint() { } private void updateVideoSeekPreviewPosition() { - int x = videoPlayerSeekbar.getThumbX() + AndroidUtilities.dp(2) - videoPreviewFrame.getMeasuredWidth() / 2; - int min = AndroidUtilities.dp(10); - int max = videoPlayerControlFrameLayout.getMeasuredWidth() - AndroidUtilities.dp(10) - videoPreviewFrame.getMeasuredWidth() / 2; + int x = videoPlayerSeekbar.getThumbX() + dp(2) - videoPreviewFrame.getMeasuredWidth() / 2; + int min = dp(10); + int max = videoPlayerControlFrameLayout.getMeasuredWidth() - dp(10) - videoPreviewFrame.getMeasuredWidth() / 2; if (x < min) { x = min; } else if (x >= max) { @@ -8581,7 +8149,7 @@ private void createVideoControlsInterface() { @Override public void onSeekBarDrag(float progress) { if (videoPlayer != null || photoViewerWebView != null && photoViewerWebView.isControllable()) { - if (!inPreview && videoTimelineView.getVisibility() == View.VISIBLE) { + if (!inPreview && videoTimelineViewContainer.getVisibility() == View.VISIBLE) { progress = videoTimelineView.getLeftProgress() + (videoTimelineView.getRightProgress() - videoTimelineView.getLeftProgress()) * progress; } long duration = getVideoDuration(); @@ -8638,7 +8206,7 @@ protected void onDraw(Canvas canvas) { videoPlayerControlFrameLayout.addView(videoPlayerSeekbarView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); videoPlayerSeekbar = new VideoPlayerSeekBar(videoPlayerSeekbarView); - videoPlayerSeekbar.setHorizontalPadding(AndroidUtilities.dp(2)); + videoPlayerSeekbar.setHorizontalPadding(dp(2)); videoPlayerSeekbar.setColors(0x33ffffff, 0x33ffffff, Color.WHITE, Color.WHITE, Color.WHITE, 0x59ffffff); videoPlayerSeekbar.setDelegate(seekBarDelegate); @@ -8691,28 +8259,6 @@ public void setVisibility(int visibility) { }); } - private void openCaptionEnter() { - if (imageMoveAnimation != null || changeModeAnimation != null || currentEditMode != EDIT_MODE_NONE || sendPhotoType == SELECT_TYPE_AVATAR || sendPhotoType == SELECT_TYPE_WALLPAPER || sendPhotoType == SELECT_TYPE_QR) { - return; - } - if (!windowView.isFocusable()) { - makeFocusable(); - } - keyboardAnimationEnabled = true; - selectedPhotosListView.setEnabled(false); - photosCounterView.setRotationX(0.0f); - isPhotosListViewVisible = false; - if (captionEditText.getMessageEditText() != null) { - captionEditText.getMessageEditText().invalidateEffects(); - captionEditText.getMessageEditText().setText(captionEditText.getMessageEditText().getText()); - } - captionEditText.setTag(1); - captionEditText.openKeyboard(); - captionEditText.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); - lastTitle = actionBarContainer.getTitle(); - captionEditText.setVisibility(View.VISIBLE); - } - private int[] fixVideoWidthHeight(int w, int h) { int[] result = new int[]{w, h}; if (Build.VERSION.SDK_INT >= 21) { @@ -8887,52 +8433,59 @@ private boolean supportsSendingNewEntities() { public CharSequence captionForAllMedia; private void closeCaptionEnter(boolean apply) { - if (currentIndex < 0 || currentIndex >= imagesArrLocals.size() || captionEditText.getTag() == null) { + if (currentIndex < 0 || currentIndex >= imagesArrLocals.size() || !isCaptionOpen()) { return; } Object object = imagesArrLocals.get(currentIndex); if (apply) { - CharSequence caption = captionEditText.getFieldCharSequence(); - CharSequence[] result = new CharSequence[]{ caption }; - - if (hasCaptionForAllMedia && !TextUtils.equals(captionForAllMedia, caption) && placeProvider.getPhotoIndex(currentIndex) != 0 && placeProvider.getSelectedCount() > 0) { - hasCaptionForAllMedia = false; - } + CharSequence cs = applyCaption(); +// if (cs != null) { +// setCurrentCaption(null, cs, false, false); +// } + } +// updateCaptionTextForCurrentPhoto(object); + if (captionEdit.editText.isPopupShowing()) { + captionEdit.editText.hidePopup(true); + } + captionEdit.editText.closeKeyboard(); +// if (Build.VERSION.SDK_INT >= 19) { +// captionEditText.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); +// } + } - ArrayList entities = MediaDataController.getInstance(currentAccount).getEntities(result, supportsSendingNewEntities()); - captionForAllMedia = caption; + private CharSequence applyCaption() { + if (!isVisible() || placeProvider == null || currentIndex < 0 || currentIndex >= imagesArrLocals.size()) { + return null; + } + Object object = imagesArrLocals.get(currentIndex); - if (object instanceof MediaController.PhotoEntry) { - MediaController.PhotoEntry photoEntry = (MediaController.PhotoEntry) object; - photoEntry.caption = result[0]; - photoEntry.entities = entities; - } else if (object instanceof MediaController.SearchImage) { - MediaController.SearchImage photoEntry = (MediaController.SearchImage) object; - photoEntry.caption = result[0]; - photoEntry.entities = entities; - } + CharSequence caption = captionEdit.getText(); + CharSequence[] result = new CharSequence[] { caption }; - if (captionEditText.getFieldCharSequence().length() != 0 && !placeProvider.isPhotoChecked(currentIndex)) { - setPhotoChecked(); - } - if (placeProvider != null) { - placeProvider.onApplyCaption(caption); - } - setCurrentCaption(null, result[0], false, false); + if (hasCaptionForAllMedia && !TextUtils.equals(captionForAllMedia, caption) && placeProvider.getPhotoIndex(currentIndex) != 0 && placeProvider.getSelectedCount() > 0) { + hasCaptionForAllMedia = false; } - captionEditText.setTag(null); - if (isCurrentVideo && customTitle == null) { - actionBarContainer.setTitleAnimated(lastTitle, true, true); - actionBarContainer.setSubtitle(muteVideo ? LocaleController.getString("SoundMuted", R.string.SoundMuted) : currentSubtitle); + + ArrayList entities = MediaDataController.getInstance(currentAccount).getEntities(result, supportsSendingNewEntities()); + captionForAllMedia = caption; + + if (object instanceof MediaController.PhotoEntry) { + MediaController.PhotoEntry photoEntry = (MediaController.PhotoEntry) object; + photoEntry.caption = result[0]; + photoEntry.entities = entities; + } else if (object instanceof MediaController.SearchImage) { + MediaController.SearchImage photoEntry = (MediaController.SearchImage) object; + photoEntry.caption = result[0]; + photoEntry.entities = entities; } - updateCaptionTextForCurrentPhoto(object); - if (captionEditText.isPopupShowing()) { - captionEditText.hidePopup(); + + if (caption.length() != 0 && !placeProvider.isPhotoChecked(currentIndex)) { + setPhotoChecked(); } - captionEditText.closeKeyboard(); - if (Build.VERSION.SDK_INT >= 19) { - captionEditText.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); + if (placeProvider != null) { + placeProvider.onApplyCaption(caption); } + return result[0]; } private void updateVideoPlayerTime() { @@ -8944,7 +8497,7 @@ private void updateVideoPlayerTime() { current = 0; } long total = Math.max(0, videoPlayer.getDuration()); - if (!inPreview && videoTimelineView.getVisibility() == View.VISIBLE) { + if (!inPreview && videoTimelineViewContainer.getVisibility() == View.VISIBLE) { total *= (videoTimelineView.getRightProgress() - videoTimelineView.getLeftProgress()); current -= videoTimelineView.getLeftProgress() * total; if (current > total) { @@ -8963,7 +8516,7 @@ private void updateVideoPlayerTime() { current = 0; } long total = Math.max(0, photoViewerWebView.getVideoDuration()); - if (!inPreview && videoTimelineView.getVisibility() == View.VISIBLE) { + if (!inPreview && videoTimelineViewContainer.getVisibility() == View.VISIBLE) { total *= (videoTimelineView.getRightProgress() - videoTimelineView.getLeftProgress()); current -= videoTimelineView.getLeftProgress() * total; if (current > total) { @@ -8979,23 +8532,46 @@ private void updateVideoPlayerTime() { } String current, total; if (videoPlayerCurrentTime[0] >= 60) { - current = String.format(Locale.ROOT, "%02d:%02d:%02d", videoPlayerCurrentTime[0] / 60, videoPlayerCurrentTime[0] % 60, videoPlayerCurrentTime[1]); + current = format(videoPlayerCurrentTime[0] / 60, videoPlayerCurrentTime[0] % 60, videoPlayerCurrentTime[1]); } else { - current = String.format(Locale.ROOT, "%02d:%02d", videoPlayerCurrentTime[0], videoPlayerCurrentTime[1]); + current = format(videoPlayerCurrentTime[0], videoPlayerCurrentTime[1]); } if (videoPlayerTotalTime[0] >= 60) { - total = String.format(Locale.ROOT, "%02d:%02d:%02d", videoPlayerTotalTime[0] / 60, videoPlayerTotalTime[0] % 60, videoPlayerTotalTime[1]); + total = format(videoPlayerTotalTime[0] / 60, videoPlayerTotalTime[0] % 60, videoPlayerTotalTime[1]); } else { - total = String.format(Locale.ROOT, "%02d:%02d", videoPlayerTotalTime[0], videoPlayerTotalTime[1]); + total = format(videoPlayerTotalTime[0], videoPlayerTotalTime[1]); } - videoPlayerTime.setText(String.format(Locale.ROOT, "%s / %s", current, total)); + videoPlayerTime.setText(current + " / " + total); if (!Objects.equals(lastControlFrameDuration, total)) { lastControlFrameDuration = total; videoPlayerControlFrameLayout.requestLayout(); } } + private String format(int h, int m, int s) { + char[] str = new char[8]; + str[0] = (char) ('0' + (h >= 100 ? 99 : h) / 10); + str[1] = (char) ('0' + (h >= 100 ? 99 : h) % 10); + str[2] = ':'; + str[3] = (char) ('0' + (m >= 100 ? 99 : m) / 10); + str[4] = (char) ('0' + (m >= 100 ? 99 : m) % 10); + str[5] = ':'; + str[6] = (char) ('0' + (s >= 100 ? 99 : s) / 10); + str[7] = (char) ('0' + (s >= 100 ? 99 : s) % 10); + return new String(str); + } + + private String format(int m, int s) { + char[] str = new char[5]; + str[0] = (char) ('0' + (m >= 100 ? 99 : m) / 10); + str[1] = (char) ('0' + (m >= 100 ? 99 : m) % 10); + str[2] = ':'; + str[3] = (char) ('0' + (s >= 100 ? 99 : s) / 10); + str[4] = (char) ('0' + (s >= 100 ? 99 : s) % 10); + return new String(str); + } + private void checkBufferedProgress(float progress) { if (!isStreaming || parentActivity == null || streamingAlertShown || videoPlayer == null || currentMessageObject == null) { return; @@ -9031,10 +8607,7 @@ public void updateColors() { if (checkImageView != null) { checkImageView.setColor(getThemedColor(Theme.key_dialogFloatingButton), 0xffffffff); } - PorterDuffColorFilter filter = new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN); - if (timeItem != null && timeItem.getColorFilter() != null) { - timeItem.setColorFilter(filter); - } + PorterDuffColorFilter filter = new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY); if (paintItem != null && paintItem.getColorFilter() != null) { paintItem.setColorFilter(filter); } @@ -9062,8 +8635,8 @@ public void updateColors() { if (photoFilterView != null) { photoFilterView.updateColors(); } - if (captionEditText != null) { - captionEditText.updateColors(); + if (captionEdit != null) { + captionEdit.updateColors(resourcesProvider); } if (videoTimelineView != null) { videoTimelineView.invalidate(); @@ -9255,7 +8828,7 @@ private void updatePlayerState(boolean playWhenReady, int playbackState) { if (isCurrentVideo) { if (!videoTimelineView.isDragging()) { videoTimelineView.setProgress(videoTimelineView.getLeftProgress()); - if (!inPreview && (currentEditMode != EDIT_MODE_NONE || videoTimelineView.getVisibility() == View.VISIBLE)) { + if (!inPreview && (currentEditMode != EDIT_MODE_NONE || videoTimelineViewContainer.getVisibility() == View.VISIBLE)) { seekVideoOrWebToProgress(videoTimelineView.getLeftProgress()); } else { seekVideoOrWebToProgress(0); @@ -9272,7 +8845,7 @@ private void updatePlayerState(boolean playWhenReady, int playbackState) { } else { videoPlayerSeekbar.setProgress(0.0f); videoPlayerSeekbarView.invalidate(); - if (!inPreview && videoTimelineView.getVisibility() == View.VISIBLE) { + if (!inPreview && videoTimelineViewContainer.getVisibility() == View.VISIBLE) { seekVideoOrWebToProgress(videoTimelineView.getLeftProgress()); } else { seekVideoOrWebToProgress(0); @@ -9392,6 +8965,7 @@ public void onOrientationChanged(int orientation) { captureFrameReadyAtTime = -1; captureFrameAtTime = -1; needCaptureFrameReadyAtTime = -1; + firstFrameRendered = false; if (videoPlayer == null) { if (injectingVideoPlayer != null) { videoPlayer = injectingVideoPlayer; @@ -9421,18 +8995,48 @@ public void seekTo(long positionMs) { seekAnimatedStickersTo(positionMs); } } + + @Override + public void onRenderedFirstFrame() { + super.onRenderedFirstFrame(); + firstFrameRendered = true; + if (usedSurfaceView) { + containerView.invalidate(); + } + } }; newPlayerCreated = true; } if (videoTextureView != null) { videoPlayer.setTextureView(videoTextureView); + } else if (videoSurfaceView != null) { + videoPlayer.setSurfaceView(videoSurfaceView); } + if (firstFrameView != null) { firstFrameView.clear(); } videoPlayer.setDelegate(new VideoPlayer.VideoPlayerDelegate() { + + private boolean firstState = true; + @Override public void onStateChanged(boolean playWhenReady, int playbackState) { + if (firstState && videoPlayer != null && videoPlayer.getDuration() != C.TIME_UNSET) { + firstState = false; + if (imagesArr.isEmpty() && secureDocuments.isEmpty() && imagesArrLocations.isEmpty() && !imagesArrLocals.isEmpty() && switchingToIndex >= 0 && switchingToIndex < imagesArrLocals.size()) { + Object object = imagesArrLocals.get(switchingToIndex); + if (object instanceof MediaController.PhotoEntry) { + MediaController.PhotoEntry photoEntry = ((MediaController.PhotoEntry) object); + if (photoEntry.isVideo && photoEntry.editedInfo != null) { + videoPlayer.seekTo((long) (photoEntry.editedInfo.start * videoPlayer.getDuration())); + if (videoTimelineView != null) { + videoTimelineView.setProgress(photoEntry.editedInfo.start); + } + } + } + } + } updatePlayerState(playWhenReady, playbackState); } @@ -9522,98 +9126,7 @@ public boolean onSurfaceDestroyed(SurfaceTexture surfaceTexture) { @Override public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { - if (waitingForFirstTextureUpload == 2) { - if (textureImageView != null) { - textureImageView.setVisibility(View.INVISIBLE); - textureImageView.setImageDrawable(null); - if (currentBitmap != null) { - currentBitmap.recycle(); - currentBitmap = null; - } - } - switchingInlineMode = false; - - if (Build.VERSION.SDK_INT >= 21) { - aspectRatioFrameLayout.getLocationInWindow(pipPosition); - //pipPosition[0] -= getLeftInset(); - pipPosition[1] -= containerView.getTranslationY(); - if (textureImageView != null) { - textureImageView.setTranslationX(textureImageView.getTranslationX() + getLeftInset()); - } - if (videoTextureView != null) { - videoTextureView.setTranslationX(videoTextureView.getTranslationX() + getLeftInset() - aspectRatioFrameLayout.getX()); - } - if (firstFrameView != null) { - firstFrameView.setTranslationX(videoTextureView.getTranslationX()); - } - - ValueAnimator progressAnimator = ValueAnimator.ofFloat(0, 1); - progressAnimator.addUpdateListener(animation -> clippingImageProgress = 1f - (float) animation.getAnimatedValue()); - - AnimatorSet animatorSet = new AnimatorSet(); - ArrayList animators = new ArrayList<>(); - animators.add(progressAnimator); - animators.add(ObjectAnimator.ofFloat(textureImageView, View.SCALE_X, 1.0f)); - animators.add(ObjectAnimator.ofFloat(textureImageView, View.SCALE_Y, 1.0f)); - animators.add(ObjectAnimator.ofFloat(textureImageView, View.TRANSLATION_X, pipPosition[0])); - animators.add(ObjectAnimator.ofFloat(textureImageView, View.TRANSLATION_Y, pipPosition[1])); - animators.add(ObjectAnimator.ofFloat(videoTextureView, View.SCALE_X, 1.0f)); - animators.add(ObjectAnimator.ofFloat(videoTextureView, View.SCALE_Y, 1.0f)); - animators.add(ObjectAnimator.ofFloat(videoTextureView, View.TRANSLATION_X, pipPosition[0] - aspectRatioFrameLayout.getX())); - animators.add(ObjectAnimator.ofFloat(videoTextureView, View.TRANSLATION_Y, pipPosition[1] - aspectRatioFrameLayout.getY())); - animators.add(ObjectAnimator.ofInt(backgroundDrawable, AnimationProperties.COLOR_DRAWABLE_ALPHA, 255)); - if (firstFrameView != null) { - animators.add(ObjectAnimator.ofFloat(firstFrameView, View.SCALE_X, 1.0f)); - animators.add(ObjectAnimator.ofFloat(firstFrameView, View.SCALE_Y, 1.0f)); - animators.add(ObjectAnimator.ofFloat(firstFrameView, View.TRANSLATION_X, pipPosition[0] - aspectRatioFrameLayout.getX())); - animators.add(ObjectAnimator.ofFloat(firstFrameView, View.TRANSLATION_Y, pipPosition[1] - aspectRatioFrameLayout.getY())); - } - - org.telegram.ui.Components.Rect pipRect = PipVideoOverlay.getPipRect(false, aspectRatioFrameLayout.getAspectRatio()); - float scale = pipRect.width / videoTextureView.getWidth(); - ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1); - valueAnimator.addUpdateListener(animation -> { - inlineOutAnimationProgress = (float) animation.getAnimatedValue(); - if (videoTextureView != null) { - videoTextureView.invalidateOutline(); - } - if (textureImageView != null) { - textureImageView.invalidateOutline(); - } - if (firstFrameView != null) { - firstFrameView.invalidateOutline(); - } - }); - animators.add(valueAnimator); - - animatorSet.playTogether(animators); - final DecelerateInterpolator interpolator = new DecelerateInterpolator(); - animatorSet.setInterpolator(interpolator); - animatorSet.setDuration(250); - animatorSet.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - pipAnimationInProgress = false; - if (videoTextureView != null) { - videoTextureView.setOutlineProvider(null); - } - if (textureImageView != null) { - textureImageView.setOutlineProvider(null); - } - if (firstFrameView != null) { - firstFrameView.setOutlineProvider(null); - } - } - }); - animatorSet.start(); - toggleActionBar(true, true, new ActionBarToggleParams().enableStatusBarAnimation(false).enableTranslationAnimation(false).animationDuration(250).animationInterpolator(interpolator)); - } else { - toggleActionBar(true, false); - //containerView.setTranslationY(0); - } - - waitingForFirstTextureUpload = 0; - } + checkChangedTextureView(false); AndroidUtilities.runOnUIThread(() -> { if (firstFrameView != null) { @@ -9626,7 +9139,13 @@ public void onAnimationEnd(Animator animation) { if (!imagesArrLocals.isEmpty()) { createVideoTextureView(savedFilterState); } - videoTextureView.setAlpha(videoCrossfadeAlpha = 0.0f); + videoCrossfadeAlpha = 0.0f; + if (videoTextureView != null) { + videoTextureView.setAlpha(videoCrossfadeAlpha); + } + if (videoSurfaceView != null){ + videoSurfaceView.setVisibility(View.INVISIBLE); + } if (paintingOverlay != null) { paintingOverlay.setAlpha(videoCrossfadeAlpha); } @@ -9637,7 +9156,7 @@ public void onAnimationEnd(Animator animation) { if (newPlayerCreated) { seekToProgressPending = seekToProgressPending2; videoPlayerSeekbar.setProgress(0); - videoTimelineView.setProgress(0); + videoTimelineView.setProgress(videoTimelineView.getLeftProgress()); videoPlayerSeekbar.setBufferedProgress(0); if (currentMessageObject != null) { @@ -9691,7 +9210,7 @@ public void onAnimationEnd(Animator animation) { if (currentBotInlineResult != null && (currentBotInlineResult.type.equals("video") || MessageObject.isVideoDocument(currentBotInlineResult.document))) { bottomLayout.setVisibility(View.VISIBLE); - bottomLayout.setPadding(0, 0, AndroidUtilities.dp(84), 0); + bottomLayout.setPadding(0, 0, dp(84), 0); pickerView.setVisibility(View.GONE); } else { bottomLayout.setPadding(0, 0, 0, 0); @@ -9762,7 +9281,7 @@ public void checkFullscreenButton() { float scale = w / (float) containerView.getMeasuredWidth(); int height = (int) (h / scale); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) fullscreenButton[b].getLayoutParams(); - layoutParams.topMargin = (containerView.getMeasuredHeight() + height) / 2 - AndroidUtilities.dp(48); + layoutParams.topMargin = (containerView.getMeasuredHeight() + height) / 2 - dp(48); } else { if (fullscreenButton[b].getVisibility() != View.INVISIBLE) { fullscreenButton[b].setVisibility(View.INVISIBLE); @@ -9779,11 +9298,11 @@ public void checkFullscreenButton() { if (b == 1) { offsetX = 0; } else if (b == 2) { - offsetX = -AndroidUtilities.displaySize.x - AndroidUtilities.dp(15) + (currentTranslationX - maxX); + offsetX = -AndroidUtilities.displaySize.x - dp(15) + (currentTranslationX - maxX); } else { offsetX = currentTranslationX < minX ? (currentTranslationX - minX) : 0; } - fullscreenButton[b].setTranslationX(offsetX + AndroidUtilities.displaySize.x - AndroidUtilities.dp(48)); + fullscreenButton[b].setTranslationX(offsetX + AndroidUtilities.displaySize.x - dp(48)); } } @@ -9804,7 +9323,12 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { videoTextureView.setPivotX(videoTextureView.getMeasuredWidth() / 2); firstFrameView.setPivotX(videoTextureView.getMeasuredWidth() / 2); } else { - videoTextureView.setPivotX(0); + if (videoTextureView != null) { + videoTextureView.setPivotX(0); + } + if (videoSurfaceView != null) { + videoSurfaceView.setPivotX(0); + } firstFrameView.setPivotX(0); } checkFullscreenButton(); @@ -9815,18 +9339,25 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { if (child == lastFrameImageView && skipLastFrameDraw) { return true; } - return super.drawChild(canvas, child, drawingTime); } }; aspectRatioFrameLayout.setWillNotDraw(false); aspectRatioFrameLayout.setVisibility(View.INVISIBLE); containerView.addView(aspectRatioFrameLayout, 0, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.CENTER)); + usedSurfaceView = false; if (imagesArrLocals.isEmpty()) { - videoTextureView = new TextureView(parentActivity); + if (ALLOW_USE_SURFACE && injectingVideoPlayerSurface == null) { + videoSurfaceView = new SurfaceView(parentActivity); + usedSurfaceView = true; + } else { + videoTextureView = new TextureView(parentActivity); + } } else { VideoEditTextureView videoEditTextureView = new VideoEditTextureView(parentActivity, videoPlayer); + blurManager.resetBitmap(); + videoEditTextureView.updateUiBlurManager(blurManager); if (savedFilterState != null) { videoEditTextureView.setDelegate(thread -> thread.setFilterGLThreadDelegate(FilterShaders.getFilterShadersDelegate(savedFilterState))); } @@ -9838,10 +9369,14 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { videoSizeSet = true; injectingVideoPlayerSurface = null; } - videoTextureView.setPivotX(0); - videoTextureView.setPivotY(0); - videoTextureView.setOpaque(false); - aspectRatioFrameLayout.addView(videoTextureView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.CENTER)); + if (videoTextureView != null) { + videoTextureView.setPivotX(0); + videoTextureView.setPivotY(0); + videoTextureView.setOpaque(false); + aspectRatioFrameLayout.addView(videoTextureView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.CENTER)); + } else { + aspectRatioFrameLayout.addView(videoSurfaceView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.CENTER)); + } firstFrameView = new FirstFrameView(parentActivity); firstFrameView.setPivotX(0); @@ -9858,6 +9393,7 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { } private void releasePlayer(boolean onClose) { + usedSurfaceView = false; if (videoPlayer != null) { cancelVideoPlayRunnable(); AndroidUtilities.cancelRunOnUIThread(setLoadingRunnable); @@ -9916,6 +9452,10 @@ private void releasePlayer(boolean onClose) { } videoTextureView = null; } + blurManager.resetBitmap(); + if (videoSurfaceView != null) { + videoSurfaceView = null; + } if (isPlaying) { isPlaying = false; AndroidUtilities.cancelRunOnUIThread(updateProgressRunnable); @@ -9987,11 +9527,11 @@ private void updateCaptionTextForCurrentPhoto(Object object) { caption = ((MediaController.SearchImage) object).caption; } if (TextUtils.isEmpty(caption)) { - captionEditText.setFieldText(""); + captionEdit.setText(""); } else { - captionEditText.setFieldText(caption); + captionEdit.setText(AnimatedEmojiSpan.cloneSpans(caption, AnimatedEmojiDrawable.CACHE_TYPE_ALERT_PREVIEW)); } - captionEditText.setAllowTextEntitiesIntersection(supportsSendingNewEntities()); + captionEdit.editText.getEditText().setAllowTextEntitiesIntersection(supportsSendingNewEntities()); } public void showAlertDialog(AlertDialog.Builder builder) { @@ -10485,6 +10025,7 @@ private void applyCurrentEditMode() { if (setImage) { centerImage.setImageBitmap(bitmap); centerImage.setOrientation(0, true); + centerBlur.destroy(); containerView.requestLayout(); } ignoreDidSetImage = false; @@ -10545,7 +10086,7 @@ private void createCropView() { photoCropView = new PhotoCropView(activityContext, resourcesProvider); photoCropView.setVisibility(View.GONE); photoCropView.onDisappear(); - int index = containerView.indexOfChild(videoTimelineView); + int index = containerView.indexOfChild(videoTimelineViewContainer); containerView.addView(photoCropView, index - 1, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, 0, 0, 0, 48)); photoCropView.setDelegate(new PhotoCropView.PhotoCropViewDelegate() { @Override @@ -10596,7 +10137,7 @@ public boolean mirror() { @Override public int getVideoThumbX() { - return (int) (AndroidUtilities.dp(16) + (videoTimelineView.getMeasuredWidth() - AndroidUtilities.dp(32)) * avatarStartProgress); + return (int) (dp(16) + (videoTimelineView.getMeasuredWidth() - dp(32)) * avatarStartProgress); } }); } @@ -10612,12 +10153,67 @@ private void startVideoPlayer() { } private void detectFaces() { + if (centerImage.getAnimation() != null || imagesArrLocals.isEmpty() || sendPhotoType == SELECT_TYPE_AVATAR) { + return; + } + String key = centerImage.getImageKey(); + if (currentImageFaceKey != null && currentImageFaceKey.equals(key)) { + return; + } + currentImageHasFace = 0; + ImageReceiver.BitmapHolder bitmap = centerImage.getBitmapSafe(); + detectFaces(key, bitmap, centerImage.getOrientation()); + } + + private void detectFaces(String key, ImageReceiver.BitmapHolder bitmap, int orientation) { + if (key == null || bitmap == null || bitmap.bitmap == null) { + return; + } +// Utilities.globalQueue.postRunnable(() -> { +// FaceDetector faceDetector = null; +// try { +// faceDetector = new FaceDetector.Builder(ApplicationLoader.applicationContext) +// .setMode(FaceDetector.FAST_MODE) +// .setLandmarkType(FaceDetector.NO_LANDMARKS) +// .setTrackingEnabled(false).build(); +// if (faceDetector.isOperational()) { +// Frame frame = new Frame.Builder().setBitmap(bitmap.bitmap).setRotation(orientation).build(); +// SparseArray faces = faceDetector.detect(frame); +// boolean hasFaces = faces != null && faces.size() != 0; +// AndroidUtilities.runOnUIThread(() -> { +// String imageKey = centerImage.getImageKey(); +// if (key.equals(imageKey)) { +// currentImageHasFace = hasFaces ? 1 : 0; +// currentImageFaceKey = key; +// } +// }); +// } else { +// if (BuildVars.LOGS_ENABLED) { +// FileLog.e("face detection is not operational"); +// } +// AndroidUtilities.runOnUIThread(() -> { +// bitmap.release(); +// String imageKey = centerImage.getImageKey(); +// if (key.equals(imageKey)) { +// currentImageHasFace = 2; +// currentImageFaceKey = key; +// } +// }); +// } +// } catch (Exception e) { +// FileLog.e(e); +// } finally { +// if (faceDetector != null) { +// faceDetector.release(); +// } +// } +// }); } 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) { + if (currentEditMode == mode || (isCurrentVideo && photoProgressViews[0].backgroundState != 3) && !isCurrentVideo && (centerImage.getBitmap() == null || photoProgressViews[0].backgroundState != -1) || changeModeAnimation != null || imageMoveAnimation != null || isCaptionOpen()) { return; } windowView.setClipChildren(mode == EDIT_MODE_FILTER); @@ -10630,9 +10226,12 @@ public void switchToEditMode(final int mode) { navigationBarColorTo = 0xcc000000; } else if (mode == EDIT_MODE_PAINT) { navigationBarColorTo = 0xff000000; + } else if (mode == EDIT_MODE_NONE && fancyShadows) { + navigationBarColorTo = 0; } else { navigationBarColorTo = 0x7f000000; } + showEditCaption(editing && savedState == null && mode == EDIT_MODE_NONE, true); navigationBar.setVisibility(mode != EDIT_MODE_FILTER ? View.VISIBLE : View.INVISIBLE); switchingToMode = mode; if (currentEditMode == EDIT_MODE_NONE) { @@ -10712,20 +10311,20 @@ public void switchToEditMode(final int mode) { translationX = getLeftInset() / 2 - getRightInset() / 2; if (sendPhotoType == SELECT_TYPE_AVATAR) { if (currentEditMode == EDIT_MODE_FILTER) { - animateToY = AndroidUtilities.dp(36); + animateToY = dp(36); } else if (currentEditMode == EDIT_MODE_PAINT) { - animateToY = -AndroidUtilities.dp(12); + animateToY = -dp(12); if (photoPaintView != null) { animateToY -= photoPaintView.getAdditionalTop() / 2f; } } } else { if (currentEditMode == EDIT_MODE_CROP) { - animateToY = AndroidUtilities.dp(24 + 32); + animateToY = dp(24 + 32); } else if (currentEditMode == EDIT_MODE_FILTER) { - animateToY = AndroidUtilities.dp(93); + animateToY = dp(93); } else if (currentEditMode == EDIT_MODE_PAINT) { - animateToY = AndroidUtilities.dp(44); + animateToY = dp(44); if (photoPaintView != null) { animateToY -= photoPaintView.getAdditionalTop() / 2f; animateToY += photoPaintView.getAdditionalBottom() / 2f; @@ -10743,7 +10342,7 @@ public void switchToEditMode(final int mode) { imageMoveAnimation = new AnimatorSet(); ArrayList animators = new ArrayList<>(4); if (currentEditMode == EDIT_MODE_CROP) { - animators.add(ObjectAnimator.ofFloat(editorDoneLayout, View.TRANSLATION_Y, AndroidUtilities.dp(48))); + animators.add(ObjectAnimator.ofFloat(editorDoneLayout, View.TRANSLATION_Y, dp(48))); animators.add(ObjectAnimator.ofFloat(PhotoViewer.this, AnimationProperties.PHOTO_VIEWER_ANIMATION_VALUE, 0, 1)); animators.add(ObjectAnimator.ofFloat(photoCropView, View.ALPHA, 0)); ValueAnimator scaleAnimator = ValueAnimator.ofFloat(0, 1); @@ -10758,13 +10357,13 @@ public void switchToEditMode(final int mode) { animators.add(scaleAnimator); } else if (currentEditMode == EDIT_MODE_FILTER) { photoFilterView.shutdown(); - animators.add(ObjectAnimator.ofFloat(photoFilterView.getToolsView(), View.TRANSLATION_Y, AndroidUtilities.dp(186))); + animators.add(ObjectAnimator.ofFloat(photoFilterView.getToolsView(), View.TRANSLATION_Y, dp(186))); animators.add(ObjectAnimator.ofFloat(photoFilterView.getCurveControl(), View.ALPHA, 0.0f)); 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 animatorY = ValueAnimator.ofFloat(photoPaintView.getOffsetTranslationY(), AndroidUtilities.dp(126)); - ValueAnimator animatorX = ValueAnimator.ofFloat(0, -AndroidUtilities.dp(12)); + ValueAnimator animatorY = ValueAnimator.ofFloat(photoPaintView.getOffsetTranslationY(), dp(126)); + ValueAnimator animatorX = ValueAnimator.ofFloat(0, -dp(12)); animatorY.addUpdateListener(animation -> photoPaintView.setOffsetTranslationY((Float) animation.getAnimatedValue(), 0, 0,false)); animatorX.addUpdateListener(animation -> photoPaintView.setOffsetTranslationX((Float) animation.getAnimatedValue())); paintingOverlay.showAll(); @@ -10806,6 +10405,13 @@ public void onAnimationEnd(Animator animation) { } imageMoveAnimation = null; currentEditMode = mode; + captionEdit.keyboardNotifier.ignore(currentEditMode != EDIT_MODE_NONE); + if (paintKeyboardNotifier != null) { + paintKeyboardNotifier.ignore(currentEditMode != EDIT_MODE_PAINT); + } + if (currentEditMode != EDIT_MODE_PAINT) { + translateY = 0; + } switchingToMode = -1; applying = false; if (sendPhotoType == SELECT_TYPE_AVATAR) { @@ -10830,6 +10436,7 @@ public void onAnimationEnd(Animator animation) { AnimatorSet animatorSet = new AnimatorSet(); ArrayList arrayList = new ArrayList<>(); arrayList.add(ObjectAnimator.ofFloat(pickerView, View.TRANSLATION_Y, 0)); + arrayList.add(ObjectAnimator.ofFloat(pickerView, View.ALPHA, 1)); arrayList.add(ObjectAnimator.ofFloat(pickerViewSendButton, View.TRANSLATION_Y, 0)); if (sendPhotoType != SELECT_TYPE_AVATAR) { arrayList.add(ObjectAnimator.ofFloat(actionBar, View.TRANSLATION_Y, 0)); @@ -10843,10 +10450,6 @@ public void onAnimationEnd(Animator animation) { } else if (sendPhotoType == SELECT_TYPE_AVATAR) { arrayList.add(ObjectAnimator.ofFloat(photoCropView, View.ALPHA, 1)); } - if (cameraItem.getTag() != null) { - cameraItem.setVisibility(View.VISIBLE); - arrayList.add(ObjectAnimator.ofFloat(cameraItem, View.ALPHA, 1)); - } if (muteItem.getTag() != null) { muteItem.setVisibility(View.VISIBLE); arrayList.add(ObjectAnimator.ofFloat(muteItem, View.ALPHA, 1)); @@ -10905,12 +10508,13 @@ public void onAnimationEnd(Animator animation) { changeModeAnimation = new AnimatorSet(); ArrayList arrayList = new ArrayList<>(); - arrayList.add(ObjectAnimator.ofFloat(pickerView, View.TRANSLATION_Y, 0, AndroidUtilities.dp(isCurrentVideo ? 154 : 96))); - arrayList.add(ObjectAnimator.ofFloat(pickerViewSendButton, View.TRANSLATION_Y, 0, AndroidUtilities.dp(isCurrentVideo ? 154 : 96))); + arrayList.add(ObjectAnimator.ofFloat(pickerView, View.TRANSLATION_Y, 0, pickerView.getHeight() + captionEdit.getEditTextHeight() + (isCurrentVideo ? dp(58) : 0))); + arrayList.add(ObjectAnimator.ofFloat(pickerView, View.ALPHA, 0)); + arrayList.add(ObjectAnimator.ofFloat(pickerViewSendButton, View.TRANSLATION_Y, 0, dp(isCurrentVideo ? 154 : 96))); arrayList.add(ObjectAnimator.ofFloat(actionBar, View.TRANSLATION_Y, 0, -actionBar.getHeight())); arrayList.add(ObjectAnimator.ofObject(navigationBar, "backgroundColor", new ArgbEvaluator(), navigationBarColorFrom, navigationBarColorTo)); if (needCaptionLayout) { - arrayList.add(ObjectAnimator.ofFloat(captionTextViewSwitcher, View.TRANSLATION_Y, 0, AndroidUtilities.dp(isCurrentVideo ? 154 : 96))); + arrayList.add(ObjectAnimator.ofFloat(captionTextViewSwitcher, View.TRANSLATION_Y, 0, dp(isCurrentVideo ? 154 : 96))); } if (sendPhotoType == 0 || sendPhotoType == 4) { arrayList.add(ObjectAnimator.ofFloat(checkImageView, View.ALPHA, 1, 0)); @@ -10919,9 +10523,6 @@ public void onAnimationEnd(Animator animation) { if (selectedPhotosListView.getVisibility() == View.VISIBLE) { arrayList.add(ObjectAnimator.ofFloat(selectedPhotosListView, View.ALPHA, 1, 0)); } - if (cameraItem.getTag() != null) { - arrayList.add(ObjectAnimator.ofFloat(cameraItem, View.ALPHA, 1, 0)); - } if (muteItem.getTag() != null) { arrayList.add(ObjectAnimator.ofFloat(muteItem, View.ALPHA, 1, 0)); } @@ -10937,11 +10538,10 @@ public void onAnimationEnd(Animator animation) { pickerView.setVisibility(View.GONE); pickerViewSendButton.setVisibility(View.GONE); doneButtonFullWidth.setVisibility(View.GONE); - cameraItem.setVisibility(View.GONE); muteItem.setVisibility(View.GONE); selectedPhotosListView.setVisibility(View.GONE); selectedPhotosListView.setAlpha(0.0f); - selectedPhotosListView.setTranslationY(-AndroidUtilities.dp(10)); + selectedPhotosListView.setTranslationY(-dp(10)); photosCounterView.setRotationX(0.0f); selectedPhotosListView.setEnabled(false); isPhotosListViewVisible = false; @@ -10985,14 +10585,14 @@ public void onAnimationEnd(Animator animation) { animateToScale = newScale / scale; animateToX = getLeftInset() / 2 - getRightInset() / 2; - animateToY = -AndroidUtilities.dp(24 + 32) + (isStatusBarVisible() ? AndroidUtilities.statusBarHeight / 2 : 0); + animateToY = -dp(24 + 32) + (isStatusBarVisible() ? AndroidUtilities.statusBarHeight / 2 : 0); animationStartTime = System.currentTimeMillis(); zoomAnimation = true; } imageMoveAnimation = new AnimatorSet(); imageMoveAnimation.playTogether( - ObjectAnimator.ofFloat(editorDoneLayout, View.TRANSLATION_Y, AndroidUtilities.dp(48), 0), + ObjectAnimator.ofFloat(editorDoneLayout, View.TRANSLATION_Y, dp(48), 0), ObjectAnimator.ofFloat(PhotoViewer.this, AnimationProperties.PHOTO_VIEWER_ANIMATION_VALUE, 0, 1), ObjectAnimator.ofFloat(photoCropView, View.ALPHA, 0, 1) ); @@ -11011,6 +10611,13 @@ public void onAnimationEnd(Animator animation) { imageMoveAnimation = null; currentEditMode = mode; + captionEdit.keyboardNotifier.ignore(currentEditMode != EDIT_MODE_NONE); + if (paintKeyboardNotifier != null) { + paintKeyboardNotifier.ignore(currentEditMode != EDIT_MODE_PAINT); + } + if (currentEditMode != EDIT_MODE_PAINT) { + translateY = 0; + } switchingToMode = -1; animateToScale = 1; animateToX = 0; @@ -11061,7 +10668,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()), true, 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, null, resourcesProvider); containerView.addView(photoFilterView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); photoFilterView.getDoneTextView().setOnClickListener(v -> { applyCurrentEditMode(); @@ -11082,13 +10689,14 @@ public void onAnimationEnd(Animator animation) { switchToEditMode(EDIT_MODE_NONE); } }); - photoFilterView.getToolsView().setTranslationY(AndroidUtilities.dp(186)); + photoFilterView.getToolsView().setTranslationY(dp(186)); } changeModeAnimation = new AnimatorSet(); ArrayList arrayList = new ArrayList<>(); - arrayList.add(ObjectAnimator.ofFloat(pickerView, View.TRANSLATION_Y, 0, AndroidUtilities.dp(isCurrentVideo ? 154 : 96))); - arrayList.add(ObjectAnimator.ofFloat(pickerViewSendButton, View.TRANSLATION_Y, 0, AndroidUtilities.dp(isCurrentVideo ? 154 : 96))); + arrayList.add(ObjectAnimator.ofFloat(pickerView, View.TRANSLATION_Y, 0, pickerView.getHeight() + captionEdit.getEditTextHeight() + (isCurrentVideo ? dp(58) : 0))); + arrayList.add(ObjectAnimator.ofFloat(pickerView, View.ALPHA, 0)); + arrayList.add(ObjectAnimator.ofFloat(pickerViewSendButton, View.TRANSLATION_Y, 0, dp(isCurrentVideo ? 154 : 96))); arrayList.add(ObjectAnimator.ofFloat(actionBar, View.TRANSLATION_Y, 0, -actionBar.getHeight())); if (sendPhotoType == 0 || sendPhotoType == 4) { arrayList.add(ObjectAnimator.ofFloat(checkImageView, View.ALPHA, 1, 0)); @@ -11099,9 +10707,6 @@ public void onAnimationEnd(Animator animation) { if (selectedPhotosListView.getVisibility() == View.VISIBLE) { arrayList.add(ObjectAnimator.ofFloat(selectedPhotosListView, View.ALPHA, 1, 0)); } - if (cameraItem.getTag() != null) { - arrayList.add(ObjectAnimator.ofFloat(cameraItem, View.ALPHA, 1, 0)); - } if (muteItem.getTag() != null) { arrayList.add(ObjectAnimator.ofFloat(muteItem, View.ALPHA, 1, 0)); } @@ -11116,14 +10721,13 @@ public void onAnimationEnd(Animator animation) { pickerViewSendButton.setVisibility(View.GONE); doneButtonFullWidth.setVisibility(View.GONE); actionBar.setVisibility(View.GONE); - cameraItem.setVisibility(View.GONE); muteItem.setVisibility(View.GONE); if (photoCropView != null) { photoCropView.setVisibility(View.INVISIBLE); } selectedPhotosListView.setVisibility(View.GONE); selectedPhotosListView.setAlpha(0.0f); - selectedPhotosListView.setTranslationY(-AndroidUtilities.dp(10)); + selectedPhotosListView.setTranslationY(-dp(10)); photosCounterView.setRotationX(0.0f); selectedPhotosListView.setEnabled(false); isPhotosListViewVisible = false; @@ -11144,10 +10748,10 @@ public void onAnimationEnd(Animator animation) { float oldScale; float newScale = Math.min(getContainerViewWidth(2) / (float) bitmapWidth, getContainerViewHeight(2) / (float) bitmapHeight); if (sendPhotoType == SELECT_TYPE_AVATAR) { - animateToY = -AndroidUtilities.dp(36); + animateToY = -dp(36); oldScale = getCropFillScale(false); } else { - animateToY = -AndroidUtilities.dp(93) + (isStatusBarVisible() ? AndroidUtilities.statusBarHeight / 2 : 0); + animateToY = -dp(93) + (isStatusBarVisible() ? AndroidUtilities.statusBarHeight / 2 : 0); if (editState.cropState != null && (editState.cropState.transformRotation == 90 || editState.cropState.transformRotation == 270)) { oldScale = Math.min(getContainerViewWidth() / (float) bitmapHeight, getContainerViewHeight() / (float) bitmapWidth); } else { @@ -11164,7 +10768,7 @@ public void onAnimationEnd(Animator animation) { imageMoveAnimation = new AnimatorSet(); imageMoveAnimation.playTogether( ObjectAnimator.ofFloat(PhotoViewer.this, AnimationProperties.PHOTO_VIEWER_ANIMATION_VALUE, 0, 1), - ObjectAnimator.ofFloat(photoFilterView.getToolsView(), View.TRANSLATION_Y, AndroidUtilities.dp(186), 0) + ObjectAnimator.ofFloat(photoFilterView.getToolsView(), View.TRANSLATION_Y, dp(186), 0) ); imageMoveAnimation.setDuration(200); imageMoveAnimation.addListener(new AnimatorListenerAdapter() { @@ -11178,6 +10782,13 @@ public void onAnimationEnd(Animator animation) { photoFilterView.init(); imageMoveAnimation = null; currentEditMode = mode; + captionEdit.keyboardNotifier.ignore(currentEditMode != EDIT_MODE_NONE); + if (paintKeyboardNotifier != null) { + paintKeyboardNotifier.ignore(currentEditMode != EDIT_MODE_PAINT); + } + if (currentEditMode != EDIT_MODE_PAINT) { + translateY = 0; + } switchingToMode = -1; animateToScale = 1; animateToX = 0; @@ -11198,12 +10809,13 @@ public void onAnimationEnd(Animator animation) { changeModeAnimation = new AnimatorSet(); ArrayList arrayList = new ArrayList<>(); - arrayList.add(ObjectAnimator.ofFloat(pickerView, View.TRANSLATION_Y, AndroidUtilities.dp(isCurrentVideo ? 154 : 96))); - arrayList.add(ObjectAnimator.ofFloat(pickerViewSendButton, View.TRANSLATION_Y, AndroidUtilities.dp(isCurrentVideo ? 154 : 96))); + arrayList.add(ObjectAnimator.ofFloat(pickerView, View.TRANSLATION_Y, pickerView.getHeight() + captionEdit.getEditTextHeight() + (isCurrentVideo ? dp(58) : 0))); + arrayList.add(ObjectAnimator.ofFloat(pickerView, View.ALPHA, 0)); + arrayList.add(ObjectAnimator.ofFloat(pickerViewSendButton, View.TRANSLATION_Y, dp(isCurrentVideo ? 154 : 96))); arrayList.add(ObjectAnimator.ofFloat(actionBar, View.TRANSLATION_Y, -actionBar.getHeight())); arrayList.add(ObjectAnimator.ofObject(navigationBar, "backgroundColor", new ArgbEvaluator(), navigationBarColorFrom, navigationBarColorTo)); if (needCaptionLayout) { - arrayList.add(ObjectAnimator.ofFloat(captionTextViewSwitcher, View.TRANSLATION_Y, AndroidUtilities.dp(isCurrentVideo ? 154 : 96))); + arrayList.add(ObjectAnimator.ofFloat(captionTextViewSwitcher, View.TRANSLATION_Y, dp(isCurrentVideo ? 154 : 96))); } if (sendPhotoType == 0 || sendPhotoType == 4) { arrayList.add(ObjectAnimator.ofFloat(checkImageView, View.ALPHA, 1, 0)); @@ -11214,9 +10826,6 @@ public void onAnimationEnd(Animator animation) { if (selectedPhotosListView.getVisibility() == View.VISIBLE) { arrayList.add(ObjectAnimator.ofFloat(selectedPhotosListView, View.ALPHA, 1, 0)); } - if (cameraItem.getTag() != null) { - arrayList.add(ObjectAnimator.ofFloat(cameraItem, View.ALPHA, 1, 0)); - } if (muteItem.getTag() != null) { arrayList.add(ObjectAnimator.ofFloat(muteItem, View.ALPHA, 1, 0)); } @@ -11263,6 +10872,36 @@ private void createPaintView() { } else { state = editState.cropState; } + paintKeyboardNotifier = new KeyboardNotifier(windowView, height -> { + photoPaintView.keyboardVisible = paintKeyboardNotifier.keyboardVisible(); + containerView.invalidate(); + height = Math.max(height, photoPaintView.getEmojiPadding(false)); + translateY(photoPaintView.isCurrentText() && height > 0 ? ((AndroidUtilities.displaySize.y - height) / 2f - photoPaintView.getSelectedEntityCenterY()) / 2.5f : 0); + + if (paintKeyboardAnimator != null) { + paintKeyboardAnimator.cancel(); + } + ValueAnimator va = ValueAnimator.ofFloat(0, 1); + va.addUpdateListener(anm -> { + if (photoPaintView != null) { + photoPaintView.overlayLayout.invalidate(); + } + }); + AnimatorSet animatorSet = paintKeyboardAnimator = new AnimatorSet(); + animatorSet.playTogether( + ObjectAnimator.ofFloat(photoPaintView.weightChooserView, View.TRANSLATION_Y, -height / 2.5f), + ObjectAnimator.ofFloat(photoPaintView.bottomLayout, View.TRANSLATION_Y, Math.min(0, -height + dp(48 - 8))), + ObjectAnimator.ofFloat(photoPaintView.tabsLayout, View.ALPHA, height > dp(20) ? 0f : 1f), + ObjectAnimator.ofFloat(photoPaintView.cancelButton, View.ALPHA, height > dp(20) ? 0f : 1f), + ObjectAnimator.ofFloat(photoPaintView.doneButton, View.ALPHA, height > dp(20) ? 0f : 1f), + va + ); + animatorSet.setDuration(320); + animatorSet.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + animatorSet.start(); + photoPaintView.updatePlusEmojiKeyboardButton(); + }); + paintKeyboardNotifier.ignore(currentEditMode != EDIT_MODE_PAINT); 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) { @@ -11278,6 +10917,28 @@ protected void onOpenCloseStickersAlert(boolean open) { } } + @Override + protected int getPKeyboardHeight() { + if (paintKeyboardNotifier != null) { + return paintKeyboardNotifier.getKeyboardHeight(); + } + return 0; + } + + @Override + protected void onEmojiViewCloseByClick() { + if (paintKeyboardNotifier != null) { + paintKeyboardNotifier.awaitKeyboard(); + } + } + + @Override + protected void updateKeyboard() { + if (paintKeyboardNotifier != null) { + paintKeyboardNotifier.fire(); + } + } + @Override protected void didSetAnimatedSticker(RLottieDrawable drawable) { if (videoPlayer == null) { @@ -11300,9 +10961,31 @@ protected void onTextAdd() { switchToEditMode(EDIT_MODE_NONE); }); photoPaintView.getCancelView().setOnClickListener(v -> closePaintMode()); - photoPaintView.setOffsetTranslationY(AndroidUtilities.dp(126), 0, 0, false); - photoPaintView.setOffsetTranslationX(-AndroidUtilities.dp(12)); + photoPaintView.setOffsetTranslationY(dp(126), 0, 0, false); + photoPaintView.setOffsetTranslationX(-dp(12)); + } + } + + private ValueAnimator translateYAnimator; + private void translateY(float ty) { + if (translateYAnimator != null) { + translateYAnimator.cancel(); + translateYAnimator = null; } + if (currentEditMode != EDIT_MODE_PAINT) { + ty = 0; + } + translateYAnimator = ValueAnimator.ofFloat(translateY, ty); + translateYAnimator.addUpdateListener(anm -> { + translateY = (float) anm.getAnimatedValue(); + if (photoPaintView != null) { + photoPaintView.translateY(translateY); + } + containerView.invalidate(); + }); + translateYAnimator.setDuration(320); + translateYAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + translateYAnimator.start(); } private void closePaintMode() { @@ -11310,19 +10993,17 @@ private void closePaintMode() { } private void switchToPaintMode() { - makeNotFocusable(); changeModeAnimation = null; pickerView.setVisibility(View.GONE); pickerViewSendButton.setVisibility(View.GONE); doneButtonFullWidth.setVisibility(View.GONE); - cameraItem.setVisibility(View.GONE); muteItem.setVisibility(View.GONE); if (photoCropView != null) { photoCropView.setVisibility(View.INVISIBLE); } selectedPhotosListView.setVisibility(View.GONE); selectedPhotosListView.setAlpha(0.0f); - selectedPhotosListView.setTranslationY(-AndroidUtilities.dp(10)); + selectedPhotosListView.setTranslationY(-dp(10)); photosCounterView.setRotationX(0.0f); selectedPhotosListView.setEnabled(false); isPhotosListViewVisible = false; @@ -11334,6 +11015,7 @@ private void switchToPaintMode() { photosCounterView.setVisibility(View.GONE); updateActionBarTitlePadding(); } + showEditCaption(false, true); Bitmap bitmap = centerImage.getBitmap(); final float finalScale = scale; @@ -11344,7 +11026,7 @@ private void switchToPaintMode() { float oldScale; float newScale; if (sendPhotoType == SELECT_TYPE_AVATAR) { - animateToY = AndroidUtilities.dp(12); + animateToY = dp(12); if (photoPaintView != null) { animateToY += photoPaintView.getAdditionalTop() / 2f; } @@ -11354,7 +11036,7 @@ private void switchToPaintMode() { bitmapHeight = temp; } } else { - animateToY = -AndroidUtilities.dp(44) + (isStatusBarVisible() ? AndroidUtilities.statusBarHeight / 2 : 0); + animateToY = -dp(44) + (isStatusBarVisible() ? AndroidUtilities.statusBarHeight / 2 : 0); if (photoPaintView != null) { animateToY += photoPaintView.getAdditionalTop() / 2f; animateToY -= photoPaintView.getAdditionalBottom() / 2f; @@ -11380,8 +11062,8 @@ private void switchToPaintMode() { windowView.setClipChildren(true); navigationBar.setVisibility(View.INVISIBLE); imageMoveAnimation = new AnimatorSet(); - ValueAnimator animatorY = ValueAnimator.ofFloat(AndroidUtilities.dp(126), 0); - ValueAnimator animatorX = ValueAnimator.ofFloat(-AndroidUtilities.dp(12), 0); + ValueAnimator animatorY = ValueAnimator.ofFloat(dp(126), 0); + ValueAnimator animatorX = ValueAnimator.ofFloat(-dp(12), 0); animatorY.addUpdateListener(animation -> photoPaintView.setOffsetTranslationY((Float) animation.getAnimatedValue(), 0, 0,false)); animatorX.addUpdateListener(animation -> photoPaintView.setOffsetTranslationX((Float) animation.getAnimatedValue())); imageMoveAnimation.playTogether( @@ -11400,6 +11082,11 @@ public void onAnimationEnd(Animator animation) { paintingOverlay.hideEntities(); imageMoveAnimation = null; currentEditMode = EDIT_MODE_PAINT; + captionEdit.keyboardNotifier.ignore(currentEditMode != EDIT_MODE_NONE); + if (paintKeyboardNotifier != null) { + paintKeyboardNotifier.ignore(currentEditMode != EDIT_MODE_PAINT); + } + translateY = 0; switchingToMode = -1; animateToScale = scale = 1; animateToX = 0; @@ -11429,6 +11116,12 @@ private void toggleCheckImageView(boolean show) { animatorSet.playTogether(arrayList); animatorSet.setDuration(200); animatorSet.start(); + if (!show && isCaptionOpen()) { + if (captionEdit.editText.isPopupShowing()) { + captionEdit.editText.hidePopup(true); + } + captionEdit.editText.closeKeyboard(); + } } private void toggleMiniProgressInternal(final boolean show) { @@ -11726,7 +11419,7 @@ private void togglePhotosListView(boolean show, final boolean animated) { if (animated) { ArrayList arrayList = new ArrayList<>(); arrayList.add(ObjectAnimator.ofFloat(selectedPhotosListView, View.ALPHA, show ? 1.0f : 0.0f)); - arrayList.add(ObjectAnimator.ofFloat(selectedPhotosListView, View.TRANSLATION_Y, show ? 0 : -AndroidUtilities.dp(10))); + arrayList.add(ObjectAnimator.ofFloat(selectedPhotosListView, View.TRANSLATION_Y, show ? 0 : -dp(10))); arrayList.add(ObjectAnimator.ofFloat(photosCounterView, View.ROTATION_X, show ? 1.0f : 0.0f)); currentListViewAnimation = new AnimatorSet(); currentListViewAnimation.playTogether(arrayList); @@ -11745,7 +11438,7 @@ public void onAnimationEnd(Animator animation) { currentListViewAnimation.start(); } else { selectedPhotosListView.setAlpha(show ? 1.0f : 0.0f); - selectedPhotosListView.setTranslationY(show ? 0 : -AndroidUtilities.dp(10)); + selectedPhotosListView.setTranslationY(show ? 0 : -dp(10)); photosCounterView.setRotationX(show ? 1.0f : 0.0f); if (!show) { selectedPhotosListView.setVisibility(View.GONE); @@ -12070,6 +11763,10 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca totalImagesCount = 0; totalImagesCountMerge = 0; currentEditMode = EDIT_MODE_NONE; + captionEdit.keyboardNotifier.ignore(currentEditMode != EDIT_MODE_NONE); + if (paintKeyboardNotifier != null) { + paintKeyboardNotifier.ignore(currentEditMode != EDIT_MODE_PAINT); + } isFirstLoading = true; needSearchImageInArr = false; loadingMoreImages = false; @@ -12090,6 +11787,9 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca avatarsArr.clear(); secureDocuments.clear(); imagesArrLocals.clear(); + if (blurManager != null) { + blurManager.resetBitmap(); + } if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { actionBar.setElevation(0); } @@ -12103,13 +11803,6 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca if (currentThumb != null) { currentThumb.release(); } - if (videoTimelineView != null) { - if (sendPhotoType == SELECT_TYPE_AVATAR) { - videoTimelineView.setBackground(null); - } else { - videoTimelineView.setBackgroundColor(0x7f000000); - } - } currentThumb = object != null ? object.thumb : null; isEvent = object != null && object.isEvent; sharedMediaType = MediaDataController.MEDIA_PHOTOVIDEO; @@ -12122,8 +11815,6 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca photoCropView.setSubtitle(null); } actionBar.setBackgroundColor(Theme.ACTION_BAR_PHOTO_VIEWER_COLOR); - cameraItem.setVisibility(View.GONE); - cameraItem.setTag(null); bottomLayout.setVisibility(View.GONE); bottomLayout.setTag(0); if (countView != null) { @@ -12181,7 +11872,7 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca cropItem.setVisibility(View.GONE); tuneItem.setVisibility(View.GONE); tuneItem.setTag(null); - timeItem.setVisibility(View.GONE); + captionEdit.setTimerVisible(false, false); rotateItem.setVisibility(View.GONE); mirrorItem.setVisibility(View.GONE); @@ -12189,10 +11880,11 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca docInfoTextView.setVisibility(View.GONE); docNameTextView.setVisibility(View.GONE); showVideoTimeline(false, false); + showEditCaption(false, false); videoAvatarTooltip.setVisibility(View.GONE); compressItem.setVisibility(View.GONE); - captionEditText.setVisibility(View.GONE); - mentionListView.setVisibility(View.GONE); +// captionEditText.setVisibility(View.GONE); +// mentionListView.setVisibility(View.GONE); AndroidUtilities.updateViewVisibilityAnimated(muteItem, false, 1f, false); actionBarContainer.setSubtitle(null); @@ -12384,10 +12076,7 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca photosCounterView.setVisibility(View.VISIBLE); updateActionBarTitlePadding(); } - if (sendPhotoType != SELECT_TYPE_NO_SELECT && (sendPhotoType == 2 || sendPhotoType == 5) && placeProvider.canCaptureMorePhotos()) { - cameraItem.setVisibility(View.VISIBLE); - cameraItem.setTag(1); - } + captionEdit.setAddPhotoVisible(sendPhotoType != SELECT_TYPE_NO_SELECT && (sendPhotoType == 2 || sendPhotoType == 5) && placeProvider.canCaptureMorePhotos(), false); menuItem.setVisibility(View.GONE); imagesArrLocals.addAll(photos); Object obj = imagesArrLocals.get(index); @@ -12403,14 +12092,14 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca mirrorItem.setVisibility(View.GONE); docInfoTextView.setVisibility(View.VISIBLE); docNameTextView.setVisibility(View.VISIBLE); - pickerView.getLayoutParams().height = AndroidUtilities.dp(84); + pickerView.getLayoutParams().height = dp(84); } else if (((MediaController.PhotoEntry) obj).isVideo) { cropItem.setVisibility(View.GONE); rotateItem.setVisibility(View.GONE); mirrorItem.setVisibility(View.GONE); bottomLayout.setVisibility(View.VISIBLE); bottomLayout.setTag(1); - bottomLayout.setTranslationY(-AndroidUtilities.dp(48)); + bottomLayout.setTranslationY(-dp(48)); } else { cropItem.setVisibility(sendPhotoType != SELECT_TYPE_AVATAR ? View.VISIBLE : View.GONE); rotateItem.setVisibility(sendPhotoType != SELECT_TYPE_AVATAR ? View.GONE : View.VISIBLE); @@ -12428,23 +12117,28 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca mirrorItem.setVisibility(View.GONE); allowCaption = cropItem.getVisibility() == View.VISIBLE; } - if (parentChatActivity != null) { - mentionsAdapter.setChatInfo(parentChatActivity.chatInfo); - mentionsAdapter.setNeedUsernames(parentChatActivity.currentChat != null); - mentionsAdapter.setNeedBotContext(false); - needCaptionLayout = allowCaption && (placeProvider == null || placeProvider.allowCaption()); - captionEditText.setVisibility(needCaptionLayout ? View.VISIBLE : View.GONE); - if (needCaptionLayout) { - captionEditText.onCreate(); - } - } - if (parentAlert != null && parentAlert.allowEnterCaption) { - needCaptionLayout = allowCaption && (placeProvider == null || placeProvider.allowCaption()); - captionEditText.setVisibility(needCaptionLayout ? View.VISIBLE : View.GONE); - if (needCaptionLayout) { - captionEditText.onCreate(); - } - } + needCaptionLayout = allowCaption && (placeProvider == null || placeProvider.allowCaption()); + if (parentChatActivity != null && captionEdit.mentionContainer != null) { + captionEdit.mentionContainer.getAdapter().setChatInfo(parentChatActivity.chatInfo); + captionEdit.mentionContainer.getAdapter().setNeedUsernames(parentChatActivity.currentChat != null); + captionEdit.mentionContainer.getAdapter().setNeedBotContext(false); + } +// if (parentChatActivity != null) { +// mentionsAdapter.setChatInfo(parentChatActivity.chatInfo); +// mentionsAdapter.setNeedUsernames(parentChatActivity.currentChat != null); +// mentionsAdapter.setNeedBotContext(false); +// captionEditText.setVisibility(needCaptionLayout ? View.VISIBLE : View.GONE); +// if (needCaptionLayout) { +// 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()) { @@ -12454,7 +12148,7 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca pickerViewSendButton.setTranslationY(0); pickerViewSendButton.setAlpha(1.0f); } - if (navigationBar != null) { + if (navigationBar != null && !fancyShadows) { navigationBar.setVisibility(View.VISIBLE); navigationBar.setAlpha(1.0f); } @@ -12509,9 +12203,7 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca } else if (allowTimeItem && entry instanceof MediaController.SearchImage) { allowTimeItem = ((MediaController.SearchImage) entry).type == 0; } - if (allowTimeItem) { - timeItem.setVisibility(View.VISIBLE); - } + captionEdit.setTimerVisible(allowTimeItem, true); } checkFullscreenButton(); } @@ -12558,10 +12250,10 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated transitionSet.setDuration(220); transitionSet.setInterpolator(CubicBezierInterpolator.DEFAULT); TransitionManager.beginDelayedTransition(itemsLayout, transitionSet); - } CharSequence title = null; + editing = false; if (!imagesArr.isEmpty()) { if (switchingToIndex < 0 || switchingToIndex >= imagesArr.size()) { return; @@ -12919,6 +12611,7 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated menuItem.showSubItem(gallery_menu_share); menuItem.checkHideMenuItem(); groupedPhotosListView.fillList(); + editing = needCaptionLayout && (sendPhotoType == 0 || sendPhotoType == 2 || sendPhotoType == SELECT_TYPE_NO_SELECT); } else if (!imagesArrLocals.isEmpty()) { if (index < 0 || index >= imagesArrLocals.size()) { return; @@ -13115,8 +12808,9 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated } updateCaptionTextForCurrentPhoto(object); - PorterDuffColorFilter filter = new PorterDuffColorFilter(getThemedColor(Theme.key_dialogFloatingButton), PorterDuff.Mode.SRC_IN); - timeItem.setColorFilter(ttl != 0 ? filter : null); + PorterDuffColorFilter filter = new PorterDuffColorFilter(getThemedColor(Theme.key_dialogFloatingButton), PorterDuff.Mode.MULTIPLY); + captionEdit.setIsVideo(isVideo); + captionEdit.setTimer(ttl); paintItem.setColorFilter(isPainted ? filter : null); cropItem.setColorFilter(isCropped ? filter : null); tuneItem.setColorFilter(isFiltered ? filter : null); @@ -13126,6 +12820,7 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated mirrorItem.setColorFilter(cropState != null && cropState.mirrored ? filter : null); } rotateItem.setColorFilter(cropState != null && cropState.transformRotation != 0 ? filter : null); + editing = needCaptionLayout && (sendPhotoType == 0 || sendPhotoType == 2 || sendPhotoType == SELECT_TYPE_NO_SELECT); } else if (pageBlocksAdapter != null) { final int size = pageBlocksAdapter.getItemsCount(); if (switchingToIndex < 0 || switchingToIndex >= size) { @@ -13190,7 +12885,10 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated menuItem.hideSubItem(gallery_menu_translate); menuItem.hideSubItem(gallery_menu_hide_translation); } - + fancyShadows = editing && setAvatarFor == null; + actionBar.setBackgroundColor(fancyShadows || setAvatarFor != null ? 0 : Theme.ACTION_BAR_PHOTO_VIEWER_COLOR); + actionBarContainer.setTextShadows(fancyShadows); + navigationBar.setVisibility(fancyShadows ? View.GONE : View.VISIBLE); if (title != null) { if (animated) { if (wasIndex == index) { @@ -13227,7 +12925,7 @@ private CharSequence postProcessTranslated(MessageObject messageObject) { return ""; } CharSequence message = new SpannableStringBuilder(messageObject.messageOwner.translatedText.text); - message = Emoji.replaceEmoji(message, Theme.chat_msgTextPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false); + message = Emoji.replaceEmoji(message, Theme.chat_msgTextPaint.getFontMetricsInt(), dp(20), false); message = MessageObject.replaceAnimatedEmoji(message, messageObject.messageOwner.translatedText.entities, Theme.chat_msgTextPaint.getFontMetricsInt(), false); if (MessageObject.containsUrls(message)) { try { @@ -13246,18 +12944,58 @@ private CharSequence postProcessTranslated(MessageObject messageObject) { return message; } + private ObjectAnimator captionAnimator; + private void showEditCaption(boolean show, boolean animated) { + if (!animated) { + captionEdit.animate().setListener(null).cancel(); + captionEdit.setVisibility(show ? View.VISIBLE : View.GONE); + captionEdit.setTranslationY(0); + captionEdit.setAlpha(pickerView.getAlpha()); + } else { + if (show && captionEdit.getTag() == null) { + if (captionEdit.getVisibility() != View.VISIBLE) { + captionEdit.setVisibility(View.VISIBLE); + captionEdit.setAlpha(pickerView.getAlpha()); + captionEdit.setTranslationY(dp(58)); + } + if (captionAnimator != null) { + captionAnimator.removeAllListeners(); + captionAnimator.cancel(); + } + captionAnimator = ObjectAnimator.ofFloat(captionEdit, View.TRANSLATION_Y, captionEdit.getTranslationY(), 0); + captionAnimator.setDuration(220); + captionAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + captionAnimator.start(); + + } else if (!show && captionEdit.getTag() != null) { + if (captionAnimator != null) { + captionAnimator.removeAllListeners(); + captionAnimator.cancel(); + } + + captionAnimator = ObjectAnimator.ofFloat(captionEdit, View.TRANSLATION_Y, captionEdit.getTranslationY(), dp(58)); + captionAnimator.addListener(new HideViewAfterAnimation(captionEdit)); + captionAnimator.setDuration(220); + captionAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + captionAnimator.start(); + } + } + captionEdit.setTag(show ? 1 : null); + } + + private ObjectAnimator videoTimelineAnimator; private void showVideoTimeline(boolean show, boolean animated) { - if (!animated) { - videoTimelineView.animate().setListener(null).cancel(); - videoTimelineView.setVisibility(show ? View.VISIBLE : View.GONE); + if (!animated) { + videoTimelineViewContainer.animate().setListener(null).cancel(); + videoTimelineViewContainer.setVisibility(show ? View.VISIBLE : View.GONE); videoTimelineView.setTranslationY(0); - videoTimelineView.setAlpha(pickerView.getAlpha()); + videoTimelineViewContainer.setAlpha(pickerView.getAlpha()); } else { - if (show && videoTimelineView.getTag() == null) { - if (videoTimelineView.getVisibility() != View.VISIBLE) { - videoTimelineView.setVisibility(View.VISIBLE); - videoTimelineView.setAlpha(pickerView.getAlpha()); - videoTimelineView.setTranslationY(AndroidUtilities.dp(58)); + if (show && videoTimelineViewContainer.getTag() == null) { + if (videoTimelineViewContainer.getVisibility() != View.VISIBLE) { + videoTimelineViewContainer.setVisibility(View.VISIBLE); + videoTimelineViewContainer.setAlpha(pickerView.getAlpha()); + videoTimelineView.setTranslationY(dp(58)); } if (videoTimelineAnimator != null) { videoTimelineAnimator.removeAllListeners(); @@ -13268,20 +13006,20 @@ private void showVideoTimeline(boolean show, boolean animated) { videoTimelineAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); videoTimelineAnimator.start(); - } else if (!show && videoTimelineView.getTag() != null) { + } else if (!show && videoTimelineViewContainer.getTag() != null) { if (videoTimelineAnimator != null) { videoTimelineAnimator.removeAllListeners(); videoTimelineAnimator.cancel(); } - videoTimelineAnimator = ObjectAnimator.ofFloat(videoTimelineView, View.TRANSLATION_Y, videoTimelineView.getTranslationY(), AndroidUtilities.dp(58)); - videoTimelineAnimator.addListener(new HideViewAfterAnimation(videoTimelineView)); + videoTimelineAnimator = ObjectAnimator.ofFloat(videoTimelineView, View.TRANSLATION_Y, videoTimelineView.getTranslationY(), dp(58)); + videoTimelineAnimator.addListener(new HideViewAfterAnimation(videoTimelineViewContainer)); videoTimelineAnimator.setDuration(220); videoTimelineAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); videoTimelineAnimator.start(); } } - videoTimelineView.setTag(show ? 1 : null); + videoTimelineViewContainer.setTag(show ? 1 : null); } public static TLRPC.FileLocation getFileLocation(ImageLocation location) { @@ -13585,6 +13323,18 @@ private void setImageIndex(int index, boolean init, boolean animateCaption) { centerImage = leftImage; leftImage = temp; + BlurringShader.ThumbBlurer temp2 = rightBlur; + rightBlur = centerBlur; + centerBlur = leftBlur; + leftBlur = temp2; + + if (centerImageInsideBlur != null) { + AnimatedFloat temp3 = centerImageInsideBlur[0]; + centerImageInsideBlur[0] = centerImageInsideBlur[1]; + centerImageInsideBlur[1] = centerImageInsideBlur[2]; + centerImageInsideBlur[2] = temp3; + } + rightImageIsVideo = prevIsVideo; rightCropTransform = prevCropTransform; rightCropState = prevCropState; @@ -13614,6 +13364,18 @@ private void setImageIndex(int index, boolean init, boolean animateCaption) { centerImage = rightImage; rightImage = temp; + BlurringShader.ThumbBlurer temp2 = leftBlur; + leftBlur = centerBlur; + centerBlur = rightBlur; + rightBlur = temp2; + + if (centerImageInsideBlur != null) { + AnimatedFloat temp3 = centerImageInsideBlur[2]; + centerImageInsideBlur[2] = centerImageInsideBlur[1]; + centerImageInsideBlur[1] = centerImageInsideBlur[0]; + centerImageInsideBlur[0] = temp3; + } + leftImageIsVideo = prevIsVideo; leftCropTransform = prevCropTransform; leftCropState = prevCropState; @@ -13654,7 +13416,15 @@ private void resetIndexForDeferredImageLoading() { } private void setCurrentCaption(MessageObject messageObject, final CharSequence _caption, boolean translating, boolean animated) { - final CharSequence caption = AnimatedEmojiSpan.cloneSpans(_caption); + final CharSequence caption = AnimatedEmojiSpan.cloneSpans(_caption, AnimatedEmojiDrawable.CACHE_TYPE_ALERT_PREVIEW); + showEditCaption(editing, animated); + if (editing || sendPhotoType == SELECT_TYPE_AVATAR) { + captionEdit.setText(caption); + captionTextViewSwitcher.setVisibility(View.GONE); + return; + } else { + captionEdit.setVisibility(View.GONE); + } if (needCaptionLayout) { if (captionTextViewSwitcher.getParent() != pickerView) { if (captionContainer != null) { @@ -13665,8 +13435,52 @@ private void setCurrentCaption(MessageObject messageObject, final CharSequence _ } } else { if (captionScrollView == null) { - captionScrollView = new CaptionScrollView(containerView.getContext()); captionContainer = new FrameLayout(containerView.getContext()); + captionTextViewSwitcher.setContainer(captionContainer); + captionScrollView = new CaptionScrollView(containerView.getContext(), captionTextViewSwitcher, captionContainer) { + @Override + protected boolean isStatusBarVisible() { + return Build.VERSION.SDK_INT >= 21 && !inBubbleMode; + } + + @Override + public void invalidate() { + super.invalidate(); + if (isActionBarVisible) { + final int scrollY = getScrollY(); + final float translationY = captionTextViewSwitcher.getTranslationY(); + + boolean buttonVisible = scrollY == 0 && translationY == 0; + boolean enalrgeIconVisible = scrollY == 0 && translationY == 0; + + if (!buttonVisible) { + final int progressBottom = photoProgressViews[0].getY() + photoProgressViews[0].size; + final int topMargin = (isStatusBarVisible() ? AndroidUtilities.statusBarHeight : 0) + ActionBar.getCurrentActionBarHeight(); + final int captionTop = captionContainer.getTop() + (int) translationY - scrollY + topMargin - dp(12); + final int enlargeIconTop = (int) fullscreenButton[0].getY(); + enalrgeIconVisible = captionTop > enlargeIconTop + dp(32); + buttonVisible = captionTop > progressBottom; + } + if (allowShowFullscreenButton) { + if (fullscreenButton[0].getTag() != null && ((Integer) fullscreenButton[0].getTag()) == 3 && enalrgeIconVisible) { + fullscreenButton[0].setTag(2); + fullscreenButton[0].animate().alpha(1).setDuration(150).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + fullscreenButton[0].setTag(null); + } + }).start(); + } else if (fullscreenButton[0].getTag() == null && !enalrgeIconVisible) { + fullscreenButton[0].setTag(3); + fullscreenButton[0].animate().alpha(0).setListener(null).setDuration(150).start(); + } + + } + photoProgressViews[0].setIndexedAlpha(2, buttonVisible ? 1f : 0f, true); + } + } + }; + captionTextViewSwitcher.setScrollView(captionScrollView); captionContainer.setClipChildren(false); captionScrollView.addView(captionContainer, new ViewGroup.LayoutParams(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); containerView.addView(captionScrollView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.BOTTOM)); @@ -13855,9 +13669,9 @@ public void onAnimationEnd(Animator animation) { if (messageObject.isVideo()) { MessageObject.addUrlsByPattern(messageObject.isOutOwner(), spannableString, false, 3, (int) messageObject.getDuration(), false); } - str = Emoji.replaceEmoji(spannableString, captionTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20), false); + str = Emoji.replaceEmoji(spannableString, captionTextView.getPaint().getFontMetricsInt(), dp(20), false); } else { - str = Emoji.replaceEmoji(new SpannableStringBuilder(caption), captionTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20), false); + str = Emoji.replaceEmoji(new SpannableStringBuilder(caption), captionTextView.getPaint().getFontMetricsInt(), dp(20), false); } captionTextViewSwitcher.setTag(str); try { @@ -14091,8 +13905,8 @@ private void checkProgress(int a, boolean scroll, boolean animated) { } } - public int getSelectiongLength() { - return captionEditText != null ? captionEditText.getSelectionLength() : 0; + public int getSelectionLength() { + return captionEdit.editText != null ? captionEdit.getSelectionLength() : 0; } private void setIndexToPaintingOverlay(int index, PaintingOverlay paintingOverlay) { @@ -14593,15 +14407,15 @@ public boolean openPhotoForSelect(final TLRPC.FileLocation fileLocation, final I if (pickerViewSendButton != null) { FrameLayout.LayoutParams layoutParams2 = (FrameLayout.LayoutParams) pickerViewSendButton.getLayoutParams(); if (type == 4 || type == 5) { - pickerViewSendButton.setImageResource(R.drawable.attach_send); - layoutParams2.bottomMargin = AndroidUtilities.dp(19); + pickerViewSendButton.setImageResource(R.drawable.msg_input_send_mini); + layoutParams2.bottomMargin = dp(7.33f); } else if (type == SELECT_TYPE_AVATAR || type == SELECT_TYPE_WALLPAPER || type == SELECT_TYPE_QR) { pickerViewSendButton.setImageResource(R.drawable.floating_check); - pickerViewSendButton.setPadding(0, AndroidUtilities.dp(1), 0, 0); - layoutParams2.bottomMargin = AndroidUtilities.dp(19); + pickerViewSendButton.setPadding(0, dp(1), 0, 0); + layoutParams2.bottomMargin = dp(7.33f); } else { - pickerViewSendButton.setImageResource(R.drawable.attach_send); - layoutParams2.bottomMargin = AndroidUtilities.dp(14); + pickerViewSendButton.setImageResource(R.drawable.msg_input_send_mini); + layoutParams2.bottomMargin = dp(2.33f); } pickerViewSendButton.setLayoutParams(layoutParams2); } @@ -14778,10 +14592,10 @@ private void sendMedia(VideoEditedInfo videoEditedInfo, boolean notify, int sche onPhotoShow(null, null, null, null, null, null, Collections.singletonList(photoEntry), 0, null); - pickerView.setTranslationY(AndroidUtilities.dp(isCurrentVideo ? 154 : 96)); - pickerViewSendButton.setTranslationY(AndroidUtilities.dp(isCurrentVideo ? 154 : 96)); + pickerView.setTranslationY(dp(isCurrentVideo ? 154 : 96)); + pickerViewSendButton.setTranslationY(dp(isCurrentVideo ? 154 : 96)); actionBar.setTranslationY(-actionBar.getHeight()); - captionTextViewSwitcher.setTranslationY(AndroidUtilities.dp(isCurrentVideo ? 154 : 96)); + captionTextViewSwitcher.setTranslationY(dp(isCurrentVideo ? 154 : 96)); createPaintView(); switchToPaintMode(); @@ -14800,6 +14614,7 @@ private boolean checkAnimation() { animationEndRunnable = null; } animationInProgress = 0; + invalidateBlur(); } } return animationInProgress != 0; @@ -14853,7 +14668,10 @@ public boolean openPhoto(final MessageObject messageObject, } final PlaceProviderObject object = provider.getPlaceForPhoto(messageObject, fileLocation, index, true); - lastInsets = null; + if (Build.VERSION.SDK_INT < 21) { + insets.top = AndroidUtilities.statusBarHeight; + insets.bottom = AndroidUtilities.navigationBarHeight; + } WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); if (attachedToWindow) { try { @@ -14884,7 +14702,7 @@ public boolean openPhoto(final MessageObject messageObject, } else { windowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_SECURE; } - windowLayoutParams.softInputMode = (useSmoothKeyboard ? WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN : WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) | WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; + windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; windowView.setFocusable(false); containerView.setFocusable(false); wm.addView(windowView, windowLayoutParams); @@ -14898,6 +14716,7 @@ public boolean openPhoto(final MessageObject messageObject, doneButtonPressed = false; closePhotoAfterSelect = true; allowShowFullscreenButton = true; + usedSurfaceView = false; parentChatActivity = chatActivity; lastTitle = null; isEmbedVideo = embedSeekTime != null; @@ -15050,8 +14869,8 @@ public boolean onPreDraw() { float xPos; if (sendPhotoType == SELECT_TYPE_AVATAR) { float statusBarHeight = (isStatusBarVisible() ? AndroidUtilities.statusBarHeight : 0); - float measuredHeight = (float) photoCropView.getMeasuredHeight() - AndroidUtilities.dp(64) - statusBarHeight; - float minSide = Math.min(photoCropView.getMeasuredWidth(), measuredHeight) - 2 * AndroidUtilities.dp(16); + float measuredHeight = (float) photoCropView.getMeasuredHeight() - dp(64) - statusBarHeight; + float minSide = Math.min(photoCropView.getMeasuredWidth(), measuredHeight) - 2 * dp(16); float centerX = photoCropView.getMeasuredWidth() / 2.0f; float centerY = statusBarHeight + measuredHeight / 2.0f; @@ -15139,6 +14958,7 @@ public boolean onPreDraw() { containerView.setLayerType(View.LAYER_TYPE_NONE, null); } animationInProgress = 0; + invalidateBlur(); transitionAnimationStartTime = 0; leftCropState = null; leftCropTransform.setViewTransform(false); @@ -15180,7 +15000,10 @@ public boolean onPreDraw() { for (int i = 0; i < animatingImageViews.length; i++) { ObjectAnimator animator = ObjectAnimator.ofFloat(animatingImageViews[i], AnimationProperties.CLIPPING_IMAGE_VIEW_PROGRESS, 0.0f, 1.0f); if (i == 0) { - animator.addUpdateListener(animation -> clippingImageProgress = 1f - (float) animation.getAnimatedValue()); + animator.addUpdateListener(animation -> { + clippingImageProgress = 1f - (float) animation.getAnimatedValue(); + invalidateBlur(); + }); } animators.add(animator); } @@ -15267,26 +15090,25 @@ public void onAnimationEnd(Animator animation) { @Override public boolean onPreDraw() { windowView.getViewTreeObserver().removeOnPreDrawListener(this); - actionBar.setTranslationY(-AndroidUtilities.dp(32)); + actionBar.setTranslationY(-dp(32)); actionBar.animate().alpha(1).translationY(0).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); - checkImageView.setTranslationY(-AndroidUtilities.dp(32)); + checkImageView.setTranslationY(-dp(32)); checkImageView.animate().alpha(1).translationY(0).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); - photosCounterView.setTranslationY(-AndroidUtilities.dp(32)); + photosCounterView.setTranslationY(-dp(32)); photosCounterView.animate().alpha(1).translationY(0).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); - pickerView.setTranslationY(AndroidUtilities.dp(32)); + pickerView.setTranslationY(dp(32)); pickerView.animate().alpha(1).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); - pickerViewSendButton.setTranslationY(AndroidUtilities.dp(32)); + pickerViewSendButton.setTranslationY(dp(32)); pickerViewSendButton.setAlpha(0f); pickerViewSendButton.animate().alpha(1).translationY(0).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); - cameraItem.setTranslationY(AndroidUtilities.dp(32)); - cameraItem.animate().alpha(1).translationY(0).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); + captionEdit.setAddPhotoVisible(true, true); - videoPreviewFrame.setTranslationY(AndroidUtilities.dp(32)); + videoPreviewFrame.setTranslationY(dp(32)); videoPreviewFrame.animate().alpha(1).translationY(0).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); containerView.setAlpha(0); @@ -15297,6 +15119,8 @@ public boolean onPreDraw() { AnimatorSet animatorSet = new AnimatorSet(); ObjectAnimator a2 = ObjectAnimator.ofFloat(pickerView, View.TRANSLATION_Y, pickerView.getTranslationY(), 0f).setDuration(220); a2.setInterpolator(CubicBezierInterpolator.DEFAULT); + ObjectAnimator a3 = ObjectAnimator.ofFloat(pickerView, View.ALPHA, 1f).setDuration(220); + a3.setInterpolator(CubicBezierInterpolator.DEFAULT); animatorSet.playTogether( ObjectAnimator.ofFloat(containerView, View.ALPHA, 0f, 1f).setDuration(220), ObjectAnimator.ofFloat(navigationBar, View.ALPHA, 0f, 1f).setDuration(220), @@ -15315,6 +15139,7 @@ public void onAnimationStart(Animator animation) { public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); animationInProgress = 0; + invalidateBlur(); backgroundDrawable.setAlpha(255); containerView.invalidate(); pickerView.setTranslationY(0f); @@ -15400,7 +15225,7 @@ private void makeFocusable() { } else { windowLayoutParams.flags = 0; } - windowLayoutParams.softInputMode = (useSmoothKeyboard ? WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN : WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) | WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; + windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; WindowManager wm1 = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); try { wm1.updateViewLayout(windowView, windowLayoutParams); @@ -15411,26 +15236,6 @@ private void makeFocusable() { containerView.setFocusable(true); } - private void makeNotFocusable() { - 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; - } else { - windowLayoutParams.flags = 0; - } - windowLayoutParams.softInputMode = (useSmoothKeyboard ? WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN : WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) | WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; - WindowManager wm1 = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); - try { - wm1.updateViewLayout(windowView, windowLayoutParams); - } catch (Exception e) { - FileLog.e(e); - } - windowView.setFocusable(false); - containerView.setFocusable(false); - } - private void requestAdjustToNothing() { windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; WindowManager wm1 = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); @@ -15442,7 +15247,7 @@ private void requestAdjustToNothing() { } private void requestAdjust() { - windowLayoutParams.softInputMode = (useSmoothKeyboard ? WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN : WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) | WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; + windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; WindowManager wm1 = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); try { wm1.updateViewLayout(windowView, windowLayoutParams); @@ -15506,6 +15311,10 @@ public void closePhoto(boolean animated, boolean fromEditMode) { savedState = null; } currentEditMode = EDIT_MODE_NONE; + captionEdit.keyboardNotifier.ignore(false); + if (paintKeyboardNotifier != null) { + paintKeyboardNotifier.ignore(currentEditMode != EDIT_MODE_PAINT); + } } if (navigationBar != null) { @@ -15518,9 +15327,9 @@ public void closePhoto(boolean animated, boolean fromEditMode) { if (parentActivity == null || !isInline && !isVisible || checkAnimation() || placeProvider == null) { return; } - if (captionEditText.hideActionMode() && !fromEditMode) { - return; - } +// if (captionEditText.hideActionMode() && !fromEditMode) { +// return; +// } if (parentActivity != null && fullscreenedByButton != 0) { parentActivity.setRequestedOrientation(prevOrientation); fullscreenedByButton = 0; @@ -15558,13 +15367,19 @@ public void closePhoto(boolean animated, boolean fromEditMode) { if (textureUploaded) { Bitmap bitmap = animation.getAnimatedBitmap(); if (bitmap != null) { - try { - Bitmap src = videoTextureView.getBitmap(bitmap.getWidth(), bitmap.getHeight()); - Canvas canvas = new Canvas(bitmap); - canvas.drawBitmap(src, 0, 0, null); - src.recycle(); - } catch (Throwable e) { - FileLog.e(e); + if (usedSurfaceView) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + AndroidUtilities.getBitmapFromSurface(videoSurfaceView, bitmap); + } + } else { + try { + Bitmap src = videoTextureView.getBitmap(bitmap.getWidth(), bitmap.getHeight()); + Canvas canvas = new Canvas(bitmap); + canvas.drawBitmap(src, 0, 0, null); + src.recycle(); + } catch (Throwable e) { + FileLog.e(e); + } } } } @@ -15585,7 +15400,7 @@ public void closePhoto(boolean animated, boolean fromEditMode) { containerView.removeView(photoViewerWebView); photoViewerWebView = null; } - captionEditText.onDestroy(); +// captionEdit.editText.onDestroy(); if (parentChatActivity != null && parentChatActivity.getFragmentView() != null) { parentChatActivity.getFragmentView().invalidate(); } @@ -15654,8 +15469,8 @@ public void closePhoto(boolean animated, boolean fromEditMode) { float scale2; if (sendPhotoType == SELECT_TYPE_AVATAR) { float statusBarHeight = (isStatusBarVisible() ? AndroidUtilities.statusBarHeight : 0); - float measuredHeight = (float) photoCropView.getMeasuredHeight() - AndroidUtilities.dp(64) - statusBarHeight; - float minSide = Math.min(photoCropView.getMeasuredWidth(), measuredHeight) - 2 * AndroidUtilities.dp(16); + float measuredHeight = (float) photoCropView.getMeasuredHeight() - dp(64) - statusBarHeight; + float minSide = Math.min(photoCropView.getMeasuredWidth(), measuredHeight) - 2 * dp(16); scaleX = minSide / layoutParams.width; scaleY = minSide / layoutParams.height; scale2 = Math.max(scaleX, scaleY); @@ -15737,7 +15552,10 @@ public void closePhoto(boolean animated, boolean fromEditMode) { for (int i = 0; i < animatingImageViews.length; i++) { ObjectAnimator animator = ObjectAnimator.ofFloat(animatingImageViews[i], AnimationProperties.CLIPPING_IMAGE_VIEW_PROGRESS, 0.0f, 1.0f); if (i == 0) { - animator.addUpdateListener(animation -> clippingImageProgress = (float) animation.getAnimatedValue()); + animator.addUpdateListener(animation -> { + clippingImageProgress = (float) animation.getAnimatedValue(); + invalidateBlur(); + }); } animators.add(animator); } @@ -15773,6 +15591,7 @@ public void closePhoto(boolean animated, boolean fromEditMode) { animationEndRunnable = null; containerView.setLayerType(View.LAYER_TYPE_NONE, null); animationInProgress = 0; + invalidateBlur(); onPhotoClosed(object); MediaController.getInstance().tryResumePausedAudio(); }; @@ -15853,6 +15672,7 @@ public void onAnimationEnd(Animator animation) { currentAnimation.removeSecondParentView(containerView); currentAnimation = null; centerImage.setImageBitmap((Drawable) null); + centerBlur.destroy(); } if (placeProvider != null && !placeProvider.canScrollAway()) { placeProvider.cancelButtonPressed(); @@ -15920,9 +15740,9 @@ public void destroyPhotoViewer() { currentThumb = null; } animatingImageView.setImageBitmap(null); - if (captionEditText != null) { - captionEditText.onDestroy(); - } +// if (captionEdit.editText != null) { +// captionEdit.editText.onDestroy(); +// } if (this == PipInstance) { PipInstance = null; } else { @@ -15973,13 +15793,15 @@ private void onPhotoClosed(PlaceProviderObject object) { } requestVideoPreview(0); if (videoTimelineView != null) { - videoTimelineView.setBackgroundColor(0x7f000000); videoTimelineView.destroy(); } hintView.hide(false, 0); centerImage.setImageBitmap((Bitmap) null); + centerBlur.destroy(); leftImage.setImageBitmap((Bitmap) null); + leftBlur.destroy(); rightImage.setImageBitmap((Bitmap) null); + rightBlur.destroy(); containerView.post(() -> { animatingImageView.setImageBitmap(null); if (object != null && !AndroidUtilities.isTablet() && object.animatingImageView != null) { @@ -16072,7 +15894,8 @@ public boolean isVisible() { private void updateMinMax(float scale) { if (aspectRatioFrameLayout != null && aspectRatioFrameLayout.getVisibility() == View.VISIBLE && textureUploaded) { - scale *= Math.min(getContainerViewWidth() / (float) videoTextureView.getMeasuredWidth(), getContainerViewHeight() / (float) videoTextureView.getMeasuredHeight()); + View view = usedSurfaceView ? videoSurfaceView : videoTextureView; + scale *= Math.min(getContainerViewWidth() / (float) view.getMeasuredWidth(), getContainerViewHeight() / (float) view.getMeasuredHeight()); } float w = centerImage.getImageWidth(); float h = centerImage.getImageHeight(); @@ -16102,20 +15925,20 @@ private void updateMinMax(float scale) { private int getAdditionX() { if (currentEditMode == EDIT_MODE_CROP || currentEditMode == EDIT_MODE_NONE && sendPhotoType == SELECT_TYPE_AVATAR) { - return AndroidUtilities.dp(16); + return dp(16); } else if (currentEditMode != EDIT_MODE_NONE && currentEditMode != EDIT_MODE_PAINT) { - return AndroidUtilities.dp(14); + return dp(14); } return 0; } private int getAdditionY() { if (currentEditMode == EDIT_MODE_CROP || currentEditMode == EDIT_MODE_NONE && sendPhotoType == SELECT_TYPE_AVATAR) { - return AndroidUtilities.dp(16) + (isStatusBarVisible() ? AndroidUtilities.statusBarHeight : 0); + return dp(16) + (isStatusBarVisible() ? AndroidUtilities.statusBarHeight : 0); } else if (currentEditMode == EDIT_MODE_PAINT) { - return AndroidUtilities.dp(8) + (isStatusBarVisible() ? AndroidUtilities.statusBarHeight : 0) + photoPaintView.getAdditionalTop(); + return dp(8) + (isStatusBarVisible() ? AndroidUtilities.statusBarHeight : 0) + photoPaintView.getAdditionalTop(); } else if (currentEditMode != EDIT_MODE_NONE) { - return AndroidUtilities.dp(14) + (isStatusBarVisible() ? AndroidUtilities.statusBarHeight : 0); + return dp(14) + (isStatusBarVisible() ? AndroidUtilities.statusBarHeight : 0); } return 0; } @@ -16127,9 +15950,9 @@ private int getContainerViewWidth() { private int getContainerViewWidth(int mode) { int width = containerView.getWidth(); if (mode == 1 || mode == 0 && sendPhotoType == SELECT_TYPE_AVATAR) { - width -= AndroidUtilities.dp(32); + width -= dp(32); } else if (mode != 0 && mode != 3) { - width -= AndroidUtilities.dp(28); + width -= dp(28); } return width; } @@ -16153,11 +15976,11 @@ private int getContainerViewHeight(boolean trueHeight, int mode) { } } if (mode == EDIT_MODE_NONE && sendPhotoType == SELECT_TYPE_AVATAR || mode == EDIT_MODE_CROP) { - height -= AndroidUtilities.dp(48 + 32 + 64); + height -= dp(48 + 32 + 64); } else if (mode == EDIT_MODE_FILTER) { - height -= AndroidUtilities.dp(154 + 60); + height -= dp(154 + 60); } else if (mode == EDIT_MODE_PAINT) { - height -= AndroidUtilities.dp(48) + photoPaintView.getAdditionalBottom() + ActionBar.getCurrentActionBarHeight() + photoPaintView.getAdditionalTop(); + height -= dp(48) + photoPaintView.getAdditionalBottom() + ActionBar.getCurrentActionBarHeight() + photoPaintView.getAdditionalTop(); } return height; } @@ -16192,7 +16015,7 @@ private boolean onTouchEvent(MotionEvent ev) { return true; } - if (captionEditText.isPopupShowing() || captionEditText.isKeyboardVisible()) { + if (captionEdit.editText.isPopupShowing() || captionEdit.editText.isKeyboardVisible()) { if (ev.getAction() == MotionEvent.ACTION_UP) { closeCaptionEnter(true); } @@ -16300,6 +16123,7 @@ private boolean onTouchEvent(MotionEvent ev) { translationX = (pinchCenterX - getContainerViewWidth() / 2) - ((pinchCenterX - getContainerViewWidth() / 2) - pinchStartX) * (scale / pinchStartScale); translationY = (pinchCenterY - getContainerViewHeight() / 2) - ((pinchCenterY - getContainerViewHeight() / 2) - pinchStartY) * (scale / pinchStartScale); updateMinMax(scale); + invalidateBlur(); containerView.invalidate(); } else if (ev.getPointerCount() == 1) { if (paintViewTouched == 1 && photoPaintView != null) { @@ -16323,7 +16147,7 @@ private boolean onTouchEvent(MotionEvent ev) { return true; } } - if (placeProvider.canScrollAway() && currentEditMode == EDIT_MODE_NONE && sendPhotoType != SELECT_TYPE_AVATAR && canDragDown && !draggingDown && scale == 1 && dy >= AndroidUtilities.dp(30) && dy / 2 > dx) { + if (placeProvider.canScrollAway() && currentEditMode == EDIT_MODE_NONE && sendPhotoType != SELECT_TYPE_AVATAR && canDragDown && !draggingDown && scale == 1 && dy >= dp(30) && dy / 2 > dx) { draggingDown = true; hidePressedDrawables(); moving = false; @@ -16342,7 +16166,7 @@ private boolean onTouchEvent(MotionEvent ev) { } else if (!invalidCoords && animationStartTime == 0) { float moveDx = moveStartX - ev.getX(); float moveDy = moveStartY - ev.getY(); - if (moving || currentEditMode != EDIT_MODE_NONE || scale == 1 && Math.abs(moveDy) + AndroidUtilities.dp(12) < Math.abs(moveDx) || scale != 1) { + if (moving || currentEditMode != EDIT_MODE_NONE || scale == 1 && Math.abs(moveDy) + dp(12) < Math.abs(moveDx) || scale != 1) { if (!moving) { moveDx = 0; moveDy = 0; @@ -16375,6 +16199,7 @@ private boolean onTouchEvent(MotionEvent ev) { if (scale != 1 || currentEditMode != EDIT_MODE_NONE) { translationY -= moveDy; } + invalidateBlur(); containerView.invalidate(); } } else { @@ -16467,11 +16292,11 @@ private boolean onTouchEvent(MotionEvent ev) { } if (currentEditMode == EDIT_MODE_NONE && sendPhotoType != SELECT_TYPE_AVATAR) { - if ((translationX < minX - getContainerViewWidth() / 3 || velocity < -AndroidUtilities.dp(650)) && rightImage.hasImageSet()) { + if ((translationX < minX - getContainerViewWidth() / 3 || velocity < -dp(650)) && rightImage.hasImageSet()) { goToNext(); return true; } - if ((translationX > maxX + getContainerViewWidth() / 3 || velocity > AndroidUtilities.dp(650)) && leftImage.hasImageSet()) { + if ((translationX > maxX + getContainerViewWidth() / 3 || velocity > dp(650)) && leftImage.hasImageSet()) { goToPrev(); return true; } @@ -16516,7 +16341,7 @@ private void goToNext() { extra = (getContainerViewWidth() - centerImage.getImageWidth()) / 2 * scale; } switchImageAfterAnimation = 1; - animateTo(scale, minX - getContainerViewWidth() - extra - AndroidUtilities.dp(30) / 2, translationY, false); + animateTo(scale, minX - getContainerViewWidth() - extra - dp(30) / 2, translationY, false); } private void goToPrev() { @@ -16525,7 +16350,7 @@ private void goToPrev() { extra = (getContainerViewWidth() - centerImage.getImageWidth()) / 2 * scale; } switchImageAfterAnimation = 2; - animateTo(scale, maxX + getContainerViewWidth() + extra + AndroidUtilities.dp(30) / 2, translationY, false); + animateTo(scale, maxX + getContainerViewWidth() + extra + dp(30) / 2, translationY, false); } private void cancelMoveZoomAnimation() { @@ -16594,6 +16419,7 @@ public List getImagesArrLocals() { public void setAnimationValue(float value) { animationValue = value; containerView.invalidate(); + invalidateBlur(); } @Keep @@ -16645,8 +16471,8 @@ private float getCropFillScale(boolean rotated) { int width = rotated ? centerImage.getBitmapHeight() : centerImage.getBitmapWidth(); int height = rotated ? centerImage.getBitmapWidth() : centerImage.getBitmapHeight(); float statusBarHeight = (isStatusBarVisible() ? AndroidUtilities.statusBarHeight : 0); - float measuredHeight = (float) photoCropView.getMeasuredHeight() - AndroidUtilities.dp(64) - statusBarHeight; - float minSide = Math.min(photoCropView.getMeasuredWidth(), measuredHeight) - 2 * AndroidUtilities.dp(16); + float measuredHeight = (float) photoCropView.getMeasuredHeight() - dp(64) - statusBarHeight; + float minSide = Math.min(photoCropView.getMeasuredWidth(), measuredHeight) - 2 * dp(16); return Math.max(minSide / width, minSide / height); } @@ -16665,6 +16491,7 @@ private void onDraw(Canvas canvas) { if (scroller.getStartY() < maxY && scroller.getStartY() > minY) { translationY = scroller.getCurrY(); } + invalidateBlur(); containerView.invalidate(); } } @@ -16721,6 +16548,7 @@ private void onDraw(Canvas canvas) { if (padImageForHorizontalInsets) { canvas.restore(); } + drawFancyShadows(canvas); return; } if (animationInProgress == 3 || !isVisible && animationInProgress != 2 && !pipAnimationInProgress) { @@ -16783,6 +16611,7 @@ private void onDraw(Canvas canvas) { if (scroller.getStartY() < maxY && scroller.getStartY() > minY) { translationY = scroller.getCurrY(); } + invalidateBlur(); containerView.invalidate(); } } @@ -16811,6 +16640,11 @@ private void onDraw(Canvas canvas) { } } + currentTranslationY += translateY; + if (currentEditMode == EDIT_MODE_PAINT) { + currentTranslationY += photoPaintView.getEmojiPadding(false) / 2f; + } + if (photoViewerWebView != null) { photoViewerWebView.setTranslationY(currentTranslationY); } @@ -16857,9 +16691,9 @@ public void onAnimationEnd(Animator animation) { sideImage = null; if (currentEditMode == EDIT_MODE_NONE && sendPhotoType != SELECT_TYPE_AVATAR) { if (scale >= 1.0f && !zoomAnimation && !zooming) { - if (currentTranslationX > maxX + AndroidUtilities.dp(5)) { + if (currentTranslationX > maxX + dp(5)) { sideImage = leftImage; - } else if (currentTranslationX < minX - AndroidUtilities.dp(5)) { + } else if (currentTranslationX < minX - dp(5)) { sideImage = rightImage; } else { groupedPhotosListView.setMoveProgress(0.0f); @@ -16873,11 +16707,11 @@ public void onAnimationEnd(Animator animation) { if (a == 1) { offsetX = 0; } else if (a == 2) { - offsetX = -containerView.getMeasuredWidth() - AndroidUtilities.dp(15) + (currentTranslationX - maxX); + offsetX = -containerView.getMeasuredWidth() - dp(15) + (currentTranslationX - maxX); } else { offsetX = currentTranslationX < minX ? (currentTranslationX - minX) : 0; } - fullscreenButton[a].setTranslationX(offsetX + containerView.getMeasuredWidth() - AndroidUtilities.dp(48)); + fullscreenButton[a].setTranslationX(offsetX + containerView.getMeasuredWidth() - dp(48)); } if (sideImage == rightImage) { @@ -16887,13 +16721,13 @@ public void onAnimationEnd(Animator animation) { if (!zoomAnimation && translateX < minX) { alpha = Math.min(1.0f, (minX - translateX) / containerWidth); scaleDiff = (1.0f - alpha) * 0.3f; - translateX = -containerWidth - AndroidUtilities.dp(30) / 2; + translateX = -containerWidth - dp(30) / 2; } if (sideImage.hasBitmapImage()) { canvas.save(); canvas.translate(containerWidth / 2, containerHeight / 2); - canvas.translate(containerWidth + AndroidUtilities.dp(30) / 2 + translateX, 0); + canvas.translate(containerWidth + dp(30) / 2 + translateX, 0); canvas.scale(1.0f - scaleDiff, 1.0f - scaleDiff); int bitmapWidth = sideImage.getBitmapWidth(); int bitmapHeight = sideImage.getBitmapHeight(); @@ -16941,7 +16775,7 @@ public void onAnimationEnd(Animator animation) { canvas.save(); canvas.translate(translateX, currentTranslationY / currentScale); - canvas.translate((containerWidth * (scale + 1) + AndroidUtilities.dp(30)) / 2, -currentTranslationY / currentScale); + canvas.translate((containerWidth * (scale + 1) + dp(30)) / 2, -currentTranslationY / currentScale); photoProgressViews[1].setScale(1.0f - scaleDiff); photoProgressViews[1].setAlpha(alpha); photoProgressViews[1].onDraw(canvas); @@ -16975,7 +16809,7 @@ public void onAnimationEnd(Animator animation) { canvas.rotate(currentRotation); if (currentEditMode == EDIT_MODE_PAINT && photoPaintView != null) { int trueH = getContainerViewHeight(true, 0); - trueH -= photoPaintView.getEmojiPadding(Math.abs(AndroidUtilities.displaySize.y + AndroidUtilities.statusBarHeight - trueH) < AndroidUtilities.dp(20)); + trueH -= photoPaintView.getEmojiPadding(Math.abs(AndroidUtilities.displaySize.y + AndroidUtilities.statusBarHeight - trueH) < dp(20)); int h = getContainerViewHeight(false, 0); canvas.translate(0, (trueH - h) / 2f * (1f - photoPaintView.adjustPanLayoutHelperProgress())); } @@ -17015,8 +16849,9 @@ public void onAnimationEnd(Animator animation) { int bitmapWidth, originalWidth; int bitmapHeight, originalHeight; if (drawTextureView && textureUploaded && videoSizeSet) { - originalWidth = bitmapWidth = videoTextureView.getMeasuredWidth(); - originalHeight = bitmapHeight = videoTextureView.getMeasuredHeight(); + View view = usedSurfaceView ? videoSurfaceView : videoTextureView; + originalWidth = bitmapWidth = view.getMeasuredWidth(); + originalHeight = bitmapHeight = view.getMeasuredHeight(); } else { originalWidth = bitmapWidth = centerImage.getBitmapWidth(); originalHeight = bitmapHeight = centerImage.getBitmapHeight(); @@ -17151,36 +16986,10 @@ public void onAnimationEnd(Animator animation) { photoPaintView.setTransform(currentScale, currentTranslationX, currentTranslationY + (sendPhotoType == SELECT_TYPE_AVATAR ? AndroidUtilities.statusBarHeight / 2f : 0) * photoPaintView.getRenderView().getScaleX(), bitmapWidth * scaleToFitX, bitmapHeight * scaleToFitX); } - if (drawCenterImage) { - boolean mirror = false; - if (!imagesArrLocals.isEmpty()) { - if (currentEditMode == EDIT_MODE_CROP || sendPhotoType == SELECT_TYPE_AVATAR) { - mirror = cropTransform.isMirrored(); - } else { - mirror = editState.cropState != null && editState.cropState.mirrored; - } - } - boolean restore = false; - if (mirror) { - canvas.save(); - canvas.scale(-1, 1); - restore = true; - } - if (currentMirror > 0) { - if (!restore) { - canvas.save(); - restore = true; - } - canvas.scale(1 - currentMirror * 2, 1f); - canvas.skew(0, 4 * currentMirror * (1f - currentMirror) * .25f); - } - if (photoViewerWebView == null || !photoViewerWebView.isLoaded()) { - centerImage.draw(canvas); - } - if (restore) { - canvas.restore(); - } + if (drawCenterImage && !usedSurfaceView) { + drawCenterImageInternal(canvas, currentMirror, alpha); } + canvas.save(); boolean restoreMirror = false; if (currentMirror > 0) { canvas.save(); @@ -17193,21 +17002,37 @@ public void onAnimationEnd(Animator animation) { canvas.scale(scale, scale); } if (drawTextureView) { - if (!videoCrossfadeStarted && textureUploaded && videoSizeSet) { + if (!videoCrossfadeStarted && ((usedSurfaceView && firstFrameRendered) || (textureUploaded && videoSizeSet))) { videoCrossfadeStarted = true; videoCrossfadeAlpha = 0.0f; videoCrossfadeAlphaLastTime = System.currentTimeMillis(); containerView.getMeasuredHeight(); } - videoTextureView.setAlpha(alpha * videoCrossfadeAlpha); + if (videoTextureView != null) { + videoTextureView.setAlpha(alpha * videoCrossfadeAlpha); + } if (videoTextureView instanceof VideoEditTextureView) { VideoEditTextureView videoEditTextureView = (VideoEditTextureView) videoTextureView; videoEditTextureView.setViewRect((containerWidth - width) / 2 + getAdditionX() + translateX, (containerHeight - height) / 2 + getAdditionY() + currentTranslationY + currentPanTranslationY, width, height); } - aspectRatioFrameLayout.draw(canvas); + if (videoSurfaceView != null && waitingForDraw == 0 && !changingTextureView && !switchingInlineMode && !pipAnimationInProgress && videoSurfaceView.getVisibility() != View.VISIBLE) { + videoSurfaceView.setVisibility(View.VISIBLE); + } + if (!usedSurfaceView || firstFrameRendered) { + aspectRatioFrameLayout.draw(canvas); + } + if (usedSurfaceView && alpha != 1f) { + if (surfaceBlackoutPaint == null) { + surfaceBlackoutPaint = new Paint(); + } + surfaceBlackoutPaint.setAlpha((int) (255 * (1f - alpha))); + canvas.drawRect(-1f, -1f, aspectRatioFrameLayout.getWidth() + 1f, aspectRatioFrameLayout.getHeight() + 1f, surfaceBlackoutPaint); + } + if (videoCrossfadeStarted && videoCrossfadeAlpha < 1.0f) { videoCrossfadeAlpha += dt / (playerInjected ? 100.0f : 200.0f); containerView.invalidate(); + invalidateBlur(); if (videoCrossfadeAlpha > 1.0f) { videoCrossfadeAlpha = 1.0f; } @@ -17226,6 +17051,11 @@ public void onAnimationEnd(Animator animation) { paintingOverlay.draw(canvas); } canvas.restore(); + + if (drawCenterImage && usedSurfaceView && videoCrossfadeAlpha != 1f) { + drawCenterImageInternal(canvas, currentMirror, (1f - videoCrossfadeAlpha) * alpha); + } + canvas.restore(); for (int a = 0; a < pressedDrawable.length; a++) { if (drawPressedDrawable[a] || pressedDrawableAlpha[a] != 0) { pressedDrawable[a].setAlpha((int) (pressedDrawableAlpha[a] * 255)); @@ -17261,7 +17091,7 @@ public void onAnimationEnd(Animator animation) { if (sideImage.hasBitmapImage()) { canvas.save(); canvas.translate(containerWidth / 2, containerHeight / 2); - canvas.translate(-(containerWidth * (scale + 1) + AndroidUtilities.dp(30)) / 2 + currentTranslationX, 0); + canvas.translate(-(containerWidth * (scale + 1) + dp(30)) / 2 + currentTranslationX, 0); int bitmapWidth = sideImage.getBitmapWidth(); int bitmapHeight = sideImage.getBitmapHeight(); if (!leftImageIsVideo && leftCropState != null && leftCropTransform.hasViewTransform()) { @@ -17307,7 +17137,7 @@ public void onAnimationEnd(Animator animation) { canvas.save(); canvas.translate(currentTranslationX, currentTranslationY / currentScale); - canvas.translate(-(containerWidth * (scale + 1) + AndroidUtilities.dp(30)) / 2, -currentTranslationY / currentScale); + canvas.translate(-(containerWidth * (scale + 1) + dp(30)) / 2, -currentTranslationY / currentScale); photoProgressViews[2].setScale(1.0f); photoProgressViews[2].setAlpha(1.0f); photoProgressViews[2].onDraw(canvas); @@ -17325,10 +17155,9 @@ public void onAnimationEnd(Animator animation) { if (waitingForDraw != 0) { waitingForDraw--; if (waitingForDraw == 0) { - if (textureImageView != null) { + if (changedTextureView != null && !usedSurfaceView) { try { - currentBitmap = Bitmaps.createBitmap(videoTextureView.getWidth(), videoTextureView.getHeight(), Bitmap.Config.ARGB_8888); - changedTextureView.getBitmap(currentBitmap); + currentBitmap = changedTextureView.getBitmap(); } catch (Throwable e) { if (currentBitmap != null) { currentBitmap.recycle(); @@ -17343,7 +17172,18 @@ public void onAnimationEnd(Animator animation) { textureImageView.setImageDrawable(null); } } - PipVideoOverlay.dismiss(true); + if (usedSurfaceView) { +// if (videoSurfaceView != null) { +// videoSurfaceView.setVisibility(View.VISIBLE); +// } + AndroidUtilities.runOnUIThread(() -> { + checkChangedTextureView(false); + PipVideoOverlay.dismiss(true, true); + }); + + } else { + PipVideoOverlay.dismiss(true); + } } else { containerView.invalidate(); } @@ -17358,23 +17198,111 @@ public void onAnimationEnd(Animator animation) { videoForwardDrawable.setBounds(aspectRatioFrameLayout.getLeft(), aspectRatioFrameLayout.getTop() - h + (int) (currentTranslationY / currentScale), aspectRatioFrameLayout.getRight(), aspectRatioFrameLayout.getBottom() + h + (int) (currentTranslationY / currentScale)); videoForwardDrawable.draw(canvas); } + + drawFancyShadows(canvas); } - private void drawProgress(Canvas canvas, float translateX, float currentScale, float currentTranslationY, float alpha) { - boolean drawProgress; - if (isCurrentVideo) { - drawProgress = (videoTimelineView == null || !videoTimelineView.isDragging()) && (sendPhotoType != SELECT_TYPE_AVATAR || manuallyPaused) && (videoPlayer == null || !videoPlayer.isPlaying()); - } else { - drawProgress = true; + private Path clipFancyShadows; + private Paint topFancyShadowPaint, bottomFancyShadowPaint; + private LinearGradient topFancyShadow, bottomFancyShadow; + private Matrix topFancyShadowMatrix, bottomFancyShadowMatrix; + + private void drawFancyShadows(Canvas canvas) { + if (!fancyShadows) { + return; } - boolean drawMiniProgress = miniProgressView.getVisibility() == View.VISIBLE || miniProgressAnimator != null; - if (drawProgress) { - final float tx = !zoomAnimation && -translateX > maxX ? translateX + maxX : 0; - float ty = currentScale == 1.0f ? currentTranslationY : 0; - float progressAlpha = alpha; - if (drawMiniProgress) { - progressAlpha *= 1f - miniProgressView.getAlpha(); - } + float maxAlpha = !SharedConfig.photoViewerBlur ? 1f : blurAlpha.set(animationInProgress == 0 || animationInProgress == 2 || animationInProgress == 3); + if (maxAlpha <= 0) { + return; + } + + int top = (int) (AndroidUtilities.statusBarHeight * 1.5f) + ActionBar.getCurrentActionBarHeight(); + int bottom = AndroidUtilities.navigationBarHeight + pickerView.getHeight() + (captionEdit.getVisibility() == View.VISIBLE ? captionEdit.getEditTextHeightClosedKeyboard() / 2 + dp(20) : 0); + + if (clipFancyShadows == null) { + clipFancyShadows = new Path(); + topFancyShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + topFancyShadowPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); + bottomFancyShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + bottomFancyShadowPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); + topFancyShadow = new LinearGradient(0, 0, 0, 16, new int[] { 0xff000000, 0 }, new float[] { 0, 1 }, Shader.TileMode.CLAMP); + bottomFancyShadow = new LinearGradient(0, 0, 0, 16, new int[] { 0, 0xff000000 }, new float[] { 0, 1 }, Shader.TileMode.CLAMP); + topFancyShadowMatrix = new Matrix(); + bottomFancyShadowMatrix = new Matrix(); + topFancyShadowPaint.setShader(topFancyShadow); + bottomFancyShadowPaint.setShader(bottomFancyShadow); + } + + canvas.saveLayerAlpha(0, 0, containerView.getWidth(), containerView.getHeight() + AndroidUtilities.navigationBarHeight, (int) (maxAlpha * (backgroundDrawable.getAlpha() - 127) * (1f / 127f * 255f)), Canvas.ALL_SAVE_FLAG); + clipFancyShadows.rewind(); + clipFancyShadows.addRect(0, 0, containerView.getWidth(), top, Path.Direction.CW); + clipFancyShadows.addRect(0, containerView.getHeight() + AndroidUtilities.navigationBarHeight - bottom, containerView.getWidth(), containerView.getHeight() + AndroidUtilities.navigationBarHeight, Path.Direction.CW); + canvas.clipPath(clipFancyShadows); + canvas.drawColor(0xff000000); + drawCaptionBlur(canvas, shadowBlurer, 0, 0, true, true, false); + canvas.save(); + topFancyShadowMatrix.reset(); + topFancyShadowMatrix.postScale(1, top / 16f); + topFancyShadow.setLocalMatrix(topFancyShadowMatrix); + topFancyShadowPaint.setAlpha(0xd0); + canvas.drawRect(0, 0, containerView.getWidth(), top, topFancyShadowPaint); + bottomFancyShadowMatrix.reset(); + bottomFancyShadowMatrix.postScale(1, bottom / 16f); + bottomFancyShadowMatrix.postTranslate(0, containerView.getHeight() - bottom + AndroidUtilities.navigationBarHeight); + bottomFancyShadow.setLocalMatrix(bottomFancyShadowMatrix); + bottomFancyShadowPaint.setAlpha(0xbb); + canvas.drawRect(0, containerView.getHeight() + AndroidUtilities.navigationBarHeight - bottom, containerView.getWidth(), containerView.getHeight() + AndroidUtilities.navigationBarHeight, bottomFancyShadowPaint); + canvas.restore(); + canvas.restore(); + } + + private void drawCenterImageInternal(Canvas canvas, float currentMirror, float alpha) { + boolean mirror = false; + if (!imagesArrLocals.isEmpty()) { + if (currentEditMode == EDIT_MODE_CROP || sendPhotoType == SELECT_TYPE_AVATAR) { + mirror = cropTransform.isMirrored(); + } else { + mirror = editState.cropState != null && editState.cropState.mirrored; + } + } + boolean restore = false; + if (mirror) { + canvas.save(); + canvas.scale(-1, 1); + restore = true; + } + if (currentMirror > 0) { + if (!restore) { + canvas.save(); + restore = true; + } + canvas.scale(1 - currentMirror * 2, 1f); + canvas.skew(0, 4 * currentMirror * (1f - currentMirror) * .25f); + } + if (photoViewerWebView == null || !photoViewerWebView.isLoaded()) { + centerImage.setAlpha(alpha); + centerImage.draw(canvas); + } + if (restore) { + canvas.restore(); + } + } + + private void drawProgress(Canvas canvas, float translateX, float currentScale, float currentTranslationY, float alpha) { + boolean drawProgress; + if (isCurrentVideo) { + drawProgress = (videoTimelineView == null || !videoTimelineView.isDragging()) && (sendPhotoType != SELECT_TYPE_AVATAR || manuallyPaused) && (videoPlayer == null || !videoPlayer.isPlaying()); + } else { + drawProgress = true; + } + boolean drawMiniProgress = miniProgressView.getVisibility() == View.VISIBLE || miniProgressAnimator != null; + if (drawProgress) { + final float tx = !zoomAnimation && -translateX > maxX ? translateX + maxX : 0; + float ty = currentScale == 1.0f ? currentTranslationY : 0; + float progressAlpha = alpha; + if (drawMiniProgress) { + progressAlpha *= 1f - miniProgressView.getAlpha(); + } if (pipAnimationInProgress) { progressAlpha *= actionBar.getAlpha(); } else if (photoProgressViews[0].backgroundState == PROGRESS_PAUSE) { @@ -17496,6 +17424,100 @@ private int[] applyCrop(Canvas canvas, int containerWidth, int containerHeight, return tempInt; } + private int[] applyCrop(Matrix matrix, int containerWidth, int containerHeight, int bitmapWidth, int bitmapHeight, float currentScale, CropTransform cropTransform, MediaController.CropState cropState) { + int originalWidth = bitmapWidth; + int originalHeight = bitmapHeight; + float scale = Math.min(containerWidth / (float) originalWidth, containerHeight / (float) originalHeight); + int rotatedWidth = originalWidth; + int rotatedHeight = originalHeight; + int orientation = cropTransform.getOrientation(); + if (orientation == 90 || orientation == 270) { + int temp = bitmapWidth; + bitmapWidth = bitmapHeight; + bitmapHeight = temp; + + temp = rotatedWidth; + rotatedWidth = rotatedHeight; + rotatedHeight = temp; + } + float cropAnimationValue; + if (sendPhotoType != SELECT_TYPE_AVATAR && (currentEditMode == EDIT_MODE_PAINT || switchingToMode == EDIT_MODE_PAINT)) { + cropAnimationValue = 1.0f; + } else if (imageMoveAnimation != null && switchingToMode != -1) { + if (currentEditMode == EDIT_MODE_CROP || switchingToMode == EDIT_MODE_CROP || (currentEditMode == EDIT_MODE_FILTER || currentEditMode == EDIT_MODE_PAINT) && switchingToMode == -1) { + cropAnimationValue = 1.0f; + } else if (switchingToMode == EDIT_MODE_NONE) { + cropAnimationValue = animationValue; + } else { + cropAnimationValue = 1.0f - animationValue; + } + } else { + cropAnimationValue = currentEditMode == EDIT_MODE_FILTER || currentEditMode == EDIT_MODE_PAINT ? 0.0f : 1.0f; + } + float cropPw = cropTransform.getCropPw(); + float cropPh = cropTransform.getCropPh(); + bitmapWidth *= cropPw + (1.0f - cropPw) * (1.0f - cropAnimationValue); + bitmapHeight *= cropPh + (1.0f - cropPh) * (1.0f - cropAnimationValue); + float scaleToFitX = containerWidth / (float) bitmapWidth; + if (scaleToFitX * bitmapHeight > containerHeight) { + scaleToFitX = containerHeight / (float) bitmapHeight; + } +// if (sendPhotoType != SELECT_TYPE_AVATAR && (currentEditMode != EDIT_MODE_CROP || switchingToMode == EDIT_MODE_NONE) && cropState != null) { +// float startW = bitmapWidth * scaleToFitX; +// float startH = bitmapHeight * scaleToFitX; +// float originalScaleToFitX = containerWidth / (float) originalWidth; +// if (originalScaleToFitX * originalHeight > containerHeight) { +// originalScaleToFitX = containerHeight / (float) originalHeight; +// } +// float finalW = originalWidth * originalScaleToFitX / currentScale; +// float finalH = originalHeight * originalScaleToFitX / currentScale; +// +// float w = startW + (finalW - startW) * (1.0f - cropAnimationValue); +// float h = startH + (finalH - startH) * (1.0f - cropAnimationValue); +// +// canvas.clipRect(-w / 2, -h / 2, w / 2, h / 2); +// } + if (sendPhotoType == SELECT_TYPE_AVATAR || cropTransform.hasViewTransform()) { + float cropScale; + if (currentEditMode == EDIT_MODE_CROP || sendPhotoType == SELECT_TYPE_AVATAR) { + float trueScale = 1.0f + (cropTransform.getTrueCropScale() - 1.0f) * (1.0f - cropAnimationValue); + cropScale = cropTransform.getScale() / trueScale; + float scaleToFit = containerWidth / (float) rotatedWidth; + if (scaleToFit * rotatedHeight > containerHeight) { + scaleToFit = containerHeight / (float) rotatedHeight; + } + cropScale *= scaleToFit / scale; + if (sendPhotoType == SELECT_TYPE_AVATAR) { + if (currentEditMode == EDIT_MODE_PAINT || switchingToMode == EDIT_MODE_PAINT) { + cropScale /= 1.0f + (cropTransform.getMinScale() - 1.0f) * (1.0f - cropAnimationValue); + } else if (switchingToMode == EDIT_MODE_NONE) { + cropScale /= cropTransform.getMinScale(); + } + } + } else { + cropScale = cropState != null ? cropState.cropScale : 1.0f; + float trueScale = 1.0f + (cropScale - 1.0f) * (1.0f - cropAnimationValue); + cropScale *= scaleToFitX / scale / trueScale; + } + + matrix.postTranslate(cropTransform.getCropAreaX() * cropAnimationValue, cropTransform.getCropAreaY() * cropAnimationValue); + matrix.postScale(cropScale, cropScale); + matrix.postTranslate(cropTransform.getCropPx() * rotatedWidth * scale * cropAnimationValue, cropTransform.getCropPy() * rotatedHeight * scale * cropAnimationValue); + float rotation = (cropTransform.getRotation() + orientation); + if (rotation > 180) { + rotation -= 360; + } + if (sendPhotoType == SELECT_TYPE_AVATAR && (currentEditMode == EDIT_MODE_PAINT || switchingToMode == EDIT_MODE_PAINT)) { + matrix.postRotate(rotation); + } else { + matrix.postRotate(rotation * cropAnimationValue); + } + } + tempInt[0] = bitmapWidth; + tempInt[1] = bitmapHeight; + return tempInt; + } + private void onActionClick(boolean download) { if (currentMessageObject == null && currentBotInlineResult == null && (pageBlocksAdapter == null || currentFileNames[0] == null) && sendPhotoType != SELECT_TYPE_NO_SELECT) { return; @@ -17670,8 +17692,8 @@ public boolean onSingleTapUp(MotionEvent e) { float x = e.getX(); float y = e.getY(); boolean rez = false; - if (x >= (getContainerViewWidth() - AndroidUtilities.dp(100)) / 2.0f && x <= (getContainerViewWidth() + AndroidUtilities.dp(100)) / 2.0f && - y >= (getContainerViewHeight() - AndroidUtilities.dp(100)) / 2.0f && y <= (getContainerViewHeight() + AndroidUtilities.dp(100)) / 2.0f) { + if (x >= (getContainerViewWidth() - dp(100)) / 2.0f && x <= (getContainerViewWidth() + dp(100)) / 2.0f && + y >= (getContainerViewHeight() - dp(100)) / 2.0f && y <= (getContainerViewHeight() + dp(100)) / 2.0f) { rez = onSingleTapConfirmed(e); } if (rez) { @@ -17735,7 +17757,7 @@ public boolean onSingleTapConfirmed(MotionEvent e) { float x = e.getX(); float y = e.getY(); if (checkImageView.getVisibility() != View.VISIBLE) { - if (SharedConfig.nextMediaTap && y > ActionBar.getCurrentActionBarHeight() + AndroidUtilities.statusBarHeight + AndroidUtilities.dp(40)) { + if (SharedConfig.nextMediaTap && y > ActionBar.getCurrentActionBarHeight() + AndroidUtilities.statusBarHeight + dp(40)) { int side = NekoConfig.disablePhotoSideAction.Bool() ? 0 : Math.min(135, containerView.getMeasuredWidth() / 8); if (x < side) { if (leftImage.hasImageSet()) { @@ -17771,8 +17793,8 @@ public boolean onSingleTapConfirmed(MotionEvent e) { if (sharedMediaType == MediaDataController.MEDIA_FILE && currentMessageObject != null) { if (!currentMessageObject.canPreviewDocument()) { - float vy = (getContainerViewHeight() - AndroidUtilities.dp(360)) / 2.0f; - if (y >= vy && y <= vy + AndroidUtilities.dp(360)) { + float vy = (getContainerViewHeight() - dp(360)) / 2.0f; + if (y >= vy && y <= vy + dp(360)) { onActionClick(true); return true; } @@ -17780,8 +17802,8 @@ public boolean onSingleTapConfirmed(MotionEvent e) { } else { if (photoProgressViews[0] != null && containerView != null) { int state = photoProgressViews[0].backgroundState; - if (x >= (getContainerViewWidth() - AndroidUtilities.dp(100)) / 2.0f && x <= (getContainerViewWidth() + AndroidUtilities.dp(100)) / 2.0f && - y >= (getContainerViewHeight() - AndroidUtilities.dp(100)) / 2.0f && y <= (getContainerViewHeight() + AndroidUtilities.dp(100)) / 2.0f) { + if (x >= (getContainerViewWidth() - dp(100)) / 2.0f && x <= (getContainerViewWidth() + dp(100)) / 2.0f && + y >= (getContainerViewHeight() - dp(100)) / 2.0f && y <= (getContainerViewHeight() + dp(100)) / 2.0f) { if (!drawTextureView) { if (state > PROGRESS_EMPTY && state <= PROGRESS_PLAY) { onActionClick(true); @@ -17816,8 +17838,8 @@ public boolean onSingleTapConfirmed(MotionEvent e) { } else if (currentBotInlineResult != null && (currentBotInlineResult.type.equals("video") || MessageObject.isVideoDocument(currentBotInlineResult.document))) { int state = photoProgressViews[0].backgroundState; if (state > 0 && state <= 3) { - if (x >= (getContainerViewWidth() - AndroidUtilities.dp(100)) / 2.0f && x <= (getContainerViewWidth() + AndroidUtilities.dp(100)) / 2.0f && - y >= (getContainerViewHeight() - AndroidUtilities.dp(100)) / 2.0f && y <= (getContainerViewHeight() + AndroidUtilities.dp(100)) / 2.0f) { + if (x >= (getContainerViewWidth() - dp(100)) / 2.0f && x <= (getContainerViewWidth() + dp(100)) / 2.0f && + y >= (getContainerViewHeight() - dp(100)) / 2.0f && y <= (getContainerViewHeight() + dp(100)) / 2.0f) { onActionClick(true); checkProgress(0, false, true); return true; @@ -17890,7 +17912,7 @@ public boolean onDoubleTap(MotionEvent e) { if (animationStartTime != 0 || animationInProgress != 0) { return false; } - if (photoProgressViews[0] != null && photoProgressViews[0].isVisible() && photoProgressViews[0].backgroundState != PROGRESS_NONE && Math.sqrt(Math.pow(AndroidUtilities.displaySize.x / 2f - e.getX(), 2) + Math.pow((AndroidUtilities.displaySize.y + AndroidUtilities.statusBarHeight) / 2f - e.getY(), 2)) < AndroidUtilities.dp(40)) { + if (photoProgressViews[0] != null && photoProgressViews[0].isVisible() && photoProgressViews[0].backgroundState != PROGRESS_NONE && Math.sqrt(Math.pow(AndroidUtilities.displaySize.x / 2f - e.getX(), 2) + Math.pow((AndroidUtilities.displaySize.y + AndroidUtilities.statusBarHeight) / 2f - e.getY(), 2)) < dp(40)) { return false; // play button } if (scale == 1.0f) { @@ -17929,10 +17951,10 @@ public boolean onDoubleTapEvent(MotionEvent e) { private QualityChooseView qualityChooseView; private PickerBottomLayoutViewer qualityPicker; private RadialProgressView progressView; + private FrameLayout videoTimelineViewContainer; private VideoTimelinePlayView videoTimelineView; private TextView videoAvatarTooltip; private AnimatorSet qualityChooseViewAnimation; - private ObjectAnimator videoTimelineAnimator; private long captureFrameAtTime = -1; private long captureFrameReadyAtTime = -1; @@ -17995,7 +18017,7 @@ public QualityChooseView(Context context) { paint = new Paint(Paint.ANTI_ALIAS_FLAG); textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); - textPaint.setTextSize(AndroidUtilities.dp(14)); + textPaint.setTextSize(dp(14)); textPaint.setColor(0xffcdcdcd); lowQualityDescription = LocaleController.getString("AccDescrVideoCompressLow", R.string.AccDescrVideoCompressLow); @@ -18036,9 +18058,9 @@ public boolean onTouchEvent(MotionEvent event) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - circleSize = AndroidUtilities.dp(8); - gapSize = AndroidUtilities.dp(2); - sideSide = AndroidUtilities.dp(18); + circleSize = dp(8); + gapSize = dp(2); + sideSide = dp(18); } @Override @@ -18048,7 +18070,7 @@ protected void onDraw(Canvas canvas) { } else { lineSize = (getMeasuredWidth() - circleSize * compressionsCount - gapSize * 2 - sideSide * 2); } - int cy = getMeasuredHeight() / 2 + AndroidUtilities.dp(6); + int cy = getMeasuredHeight() / 2 + dp(6); for (int a = 0; a < compressionsCount; a++) { int cx = sideSide + (lineSize + gapSize * 2 + circleSize) * a + circleSize / 2; if (a <= selectedCompression) { @@ -18057,19 +18079,19 @@ protected void onDraw(Canvas canvas) { paint.setColor(0x66ffffff); } - canvas.drawCircle(cx, cy, a == selectedCompression ? AndroidUtilities.dp(6) : circleSize / 2, paint); + canvas.drawCircle(cx, cy, a == selectedCompression ? dp(6) : circleSize / 2, paint); if (a != 0) { int x = cx - circleSize / 2 - gapSize - lineSize; float startPadding = a == (selectedCompression + 1) ? AndroidUtilities.dpf2(2) : 0; float endPadding = a == selectedCompression ? AndroidUtilities.dpf2(2) : 0; - canvas.drawRect(x + startPadding, cy - AndroidUtilities.dp(1), x + lineSize - endPadding, cy + AndroidUtilities.dp(2), paint); + canvas.drawRect(x + startPadding, cy - dp(1), x + lineSize - endPadding, cy + dp(2), paint); } } - canvas.drawText(lowQualityDescription, sideSide, cy - AndroidUtilities.dp(16), textPaint); + canvas.drawText(lowQualityDescription, sideSide, cy - dp(16), textPaint); float width = textPaint.measureText(hightQualityDescription); - canvas.drawText(hightQualityDescription, getMeasuredWidth() - sideSide - width, cy - AndroidUtilities.dp(16), textPaint); + canvas.drawText(hightQualityDescription, getMeasuredWidth() - sideSide - width, cy - dp(16), textPaint); } } @@ -18158,14 +18180,7 @@ private void updateVideoInfo() { actionBarContainer.setSubtitle(null); return; } - - if (selectedCompression < 2) { - compressItem.setImageResource(R.drawable.video_quality1); - } else if (selectedCompression == 2) { - compressItem.setImageResource(R.drawable.video_quality2); - } else if (selectedCompression == 3) { - compressItem.setImageResource(R.drawable.video_quality3); - } + compressItem.setState(videoConvertSupported && compressionsCount > 1, muteVideo, Math.min(resultWidth, resultHeight)); itemsLayout.requestLayout(); estimatedDuration = (long) Math.ceil((videoTimelineView.getRightProgress() - videoTimelineView.getLeftProgress()) * videoDuration); @@ -18380,16 +18395,24 @@ private void showQualityView(final boolean show) { } qualityChooseViewAnimation = new AnimatorSet(); if (show) { + if (fancyShadows) { + navigationBar.setVisibility(View.VISIBLE); + navigationBar.setAlpha(0f); + navigationBar.setBackgroundColor(0x7f000000); + } 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(pickerView, View.TRANSLATION_Y, 0, pickerView.getHeight() + captionEdit.getEditTextHeight() + (isCurrentVideo ? dp(58) : 0)), + ObjectAnimator.ofFloat(pickerView, View.ALPHA, 0), + ObjectAnimator.ofFloat(pickerViewSendButton, View.TRANSLATION_Y, 0, dp(158)), + ObjectAnimator.ofFloat(navigationBar, View.ALPHA, fancyShadows ? 0 : 1, 1) ); } 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(qualityChooseView, View.TRANSLATION_Y, 0, dp(166)), + ObjectAnimator.ofFloat(qualityPicker, View.TRANSLATION_Y, 0, dp(166)), + ObjectAnimator.ofFloat(navigationBar, View.ALPHA, 1, fancyShadows ? 0 : 1) ); } qualityChooseViewAnimation.addListener(new AnimatorListenerAdapter() { @@ -18407,10 +18430,16 @@ public void onAnimationEnd(Animator animation) { ObjectAnimator.ofFloat(qualityPicker, View.TRANSLATION_Y, 0) ); } else { + if (fancyShadows) { + navigationBar.setVisibility(View.GONE); + navigationBar.setAlpha(0f); + navigationBar.setBackgroundColor(0x7f000000); + } qualityChooseView.setVisibility(View.INVISIBLE); qualityPicker.setVisibility(View.INVISIBLE); qualityChooseViewAnimation.playTogether( ObjectAnimator.ofFloat(pickerView, View.TRANSLATION_Y, 0), + ObjectAnimator.ofFloat(pickerView, View.ALPHA, 1), ObjectAnimator.ofFloat(pickerViewSendButton, View.TRANSLATION_Y, 0) ); } @@ -18436,12 +18465,6 @@ public void onAnimationCancel(Animator animation) { qualityChooseViewAnimation.setInterpolator(AndroidUtilities.accelerateInterpolator); qualityChooseViewAnimation.start(); - if (cameraItem.getVisibility() == View.VISIBLE) { - cameraItem.animate().scaleX(show ? 0.25f : 1f) - .scaleY(show ? 0.25f : 1f) - .alpha(show ? 0 : 1) - .setDuration(200); - } if (muteItem.getVisibility() == View.VISIBLE) { muteItem.animate().scaleX(show ? 0.25f : 1f) .scaleY(show ? 0.25f : 1f) @@ -18530,13 +18553,13 @@ public void run() { selectedCompression = compressionsCount - 1; } - setCompressItemEnabled(compressionsCount > 1, true); + compressItem.setState(compressionsCount > 1, muteVideo, Math.min(resultWidth, resultHeight)); if (BuildVars.LOGS_ENABLED) { FileLog.d("compressionsCount = " + compressionsCount + " w = " + originalWidth + " h = " + originalHeight + " r = " + rotationValue); } qualityChooseView.invalidate(); } else { - setCompressItemEnabled(false, true); + compressItem.setState(false, muteVideo, Math.min(resultWidth, resultHeight)); compressionsCount = 0; } @@ -18578,33 +18601,6 @@ private void updateCompressionsCount(int h, int w) { } } - private void setCompressItemEnabled(boolean enabled, boolean animated) { - if (compressItem == null) { - return; - } - if (enabled && compressItem.getTag() != null || !enabled && compressItem.getTag() == null) { - return; - } - compressItem.setTag(enabled ? 1 : null); - if (compressItemAnimation != null) { - compressItemAnimation.cancel(); - compressItemAnimation = null; - } - if (animated) { - compressItemAnimation = new AnimatorSet(); - compressItemAnimation.playTogether( - ObjectAnimator.ofFloat(compressItem, View.ALPHA, enabled ? 1.0f : 0.5f), - ObjectAnimator.ofFloat(paintItem, View.ALPHA, videoConvertSupported ? 1.0f : 0.5f), - ObjectAnimator.ofFloat(tuneItem, View.ALPHA, videoConvertSupported ? 1.0f : 0.5f), - ObjectAnimator.ofFloat(cropItem, View.ALPHA, videoConvertSupported ? 1.0f : 0.5f)); - compressItemAnimation.setDuration(180); - compressItemAnimation.setInterpolator(decelerateInterpolator); - compressItemAnimation.start(); - } else { - compressItem.setAlpha(enabled ? 1.0f : 0.5f); - } - } - private void updateAccessibilityOverlayVisibility() { if (playButtonAccessibilityOverlay != null) { final int state = photoProgressViews[0].backgroundState; @@ -18682,7 +18678,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { PhotoPickerPhotoCell cell = (PhotoPickerPhotoCell) holder.itemView; - cell.setItemWidth(AndroidUtilities.dp(85), position != 0 ? AndroidUtilities.dp(6) : 0); + cell.setItemWidth(dp(85), position != 0 ? dp(6) : 0); BackupImageView imageView = cell.imageView; boolean showing; imageView.setOrientation(0, true); @@ -18829,9 +18825,583 @@ private void updateAlpha() { } private int getThemedColor(int key) { - if (resourcesProvider != null) { - return resourcesProvider.getColor(key); + if (resourcesProvider != null) { + return resourcesProvider.getColor(key); + } + return Theme.getColor(key); + } + + private final AnimatedFloat blurAlpha = new AnimatedFloat(this::invalidateBlur, 180, CubicBezierInterpolator.EASE_OUT); + private AnimatedFloat[] centerImageInsideBlur; + private RectF blurBounds; + private RectF imageBounds; + private Matrix imageBoundsMatrix; + private float[] imageBoundsPoints; + + private void drawCaptionBlur(Canvas canvas, BlurringShader.StoryBlurDrawer drawer, int bgColor, int overlayColor, boolean clip, boolean allowTransparent, boolean allowCrossfade) { + float maxAlpha = !SharedConfig.photoViewerBlur ? 1f : blurAlpha.set(animationInProgress == 0 || animationInProgress == 2 || animationInProgress == 3); + + drawer.paint.setShader(null); + if (bgColor != 0) { + drawer.paint.setColor(bgColor); + drawer.paint.setAlpha((int) (drawer.paint.getAlpha() * AndroidUtilities.lerp(.7f, 1f, allowTransparent ? maxAlpha : 1f))); + canvas.drawPaint(drawer.paint); + } + + if (!SharedConfig.photoViewerBlur || animationInProgress != 0) { + blurAlpha.set(0, true); + if (overlayColor != 0) { + drawer.paint.setColor(overlayColor); + drawer.paint.setAlpha((int) (drawer.paint.getAlpha() * AndroidUtilities.lerp(.7f, 1f, allowTransparent ? maxAlpha : 1f))); + canvas.drawPaint(drawer.paint); + } + return; + } + + if (allowCrossfade) { + if (centerImageInsideBlur == null) { + centerImageInsideBlur = new AnimatedFloat[3]; + centerImageInsideBlur[0] = new AnimatedFloat(this::invalidateBlur, 180, CubicBezierInterpolator.EASE_OUT); // right + centerImageInsideBlur[1] = new AnimatedFloat(this::invalidateBlur, 180, CubicBezierInterpolator.EASE_OUT); // center + centerImageInsideBlur[2] = new AnimatedFloat(this::invalidateBlur, 180, CubicBezierInterpolator.EASE_OUT); // left + } + // TODO: support left and right crossfades: + centerImageInsideBlur[0].set(1f, true); + centerImageInsideBlur[2].set(1f, true); + if (blurBounds == null) { + blurBounds = new RectF(); + } + if (imageBounds == null) { + imageBounds = new RectF(); + } + if (imageBoundsMatrix == null) { + imageBoundsMatrix = new Matrix(); + } + if (imageBoundsPoints == null) { + imageBoundsPoints = new float[8]; + } + blurBounds.set(captionEdit.getBounds()); + blurBounds.offset(captionEditContainer.getX(), captionEditContainer.getY()); + blurBounds.offset(captionEdit.getX(), captionEdit.getY()); + imageBoundsMatrix.reset(); + } + + final int restoreCount = canvas.getSaveCount(); + if (padImageForHorizontalInsets) { + canvas.save(); + canvas.translate(getLeftInset() / 2 - getRightInset() / 2, 0); + if (allowCrossfade) { + imageBoundsMatrix.preTranslate(getLeftInset() / 2 - getRightInset() / 2, 0); + } + } + + float currentTranslationY; + float currentTranslationX; + float currentScale; + float currentRotation; + float currentMirror; + if (imageMoveAnimation != null) { + currentMirror = lerp(mirror, animateToMirror, animationValue); + currentScale = lerp(scale, animateToScale, animationValue); + currentRotation = lerp(rotate, animateToRotate, animationValue); + currentTranslationY = lerp(translationY, animateToY, animationValue); + currentTranslationX = lerp(translationX, animateToX, animationValue); + } else { + currentScale = scale; + currentMirror = mirror; + currentRotation = rotate; + currentTranslationY = translationY; + currentTranslationX = translationX; + if (animationStartTime != 0) { + currentTranslationX = animateToX; + currentTranslationY = animateToY; + currentScale = animateToScale; + } + } + + int containerWidth = getContainerViewWidth(); + int containerHeight = getContainerViewHeight(); + ImageReceiver sideImage = null; + if (currentEditMode == EDIT_MODE_NONE && sendPhotoType != SELECT_TYPE_AVATAR) { + if (scale >= 1.0f && !zoomAnimation && !zooming) { + if (currentTranslationX > maxX + dp(5)) { + sideImage = leftImage; + } else if (currentTranslationX < minX - dp(5)) { + sideImage = rightImage; + } + } + } + if (sideImage == rightImage) { + float rightMaxAlpha = allowCrossfade ? centerImageInsideBlur[0].set(1f, true) : 1f; + float translateX = currentTranslationX; + float scaleDiff = 0; + float alpha = 1; + if (!zoomAnimation && translateX < minX) { + alpha = Math.min(1.0f, (minX - translateX) / containerWidth); + scaleDiff = (1.0f - alpha) * 0.3f; + translateX = -containerWidth - dp(30) / 2; + } + + if (sideImage.hasBitmapImage()) { + canvas.save(); + canvas.translate(containerWidth / 2, containerHeight / 2); + canvas.translate(containerWidth + dp(30) / 2 + translateX, 0); + canvas.scale(1.0f - scaleDiff, 1.0f - scaleDiff); + int bitmapWidth = sideImage.getBitmapWidth(); + int bitmapHeight = sideImage.getBitmapHeight(); + if (!rightImageIsVideo && rightCropState != null && rightCropTransform.hasViewTransform()) { + applyCrop(canvas, containerWidth, containerHeight, bitmapWidth, bitmapHeight, 1f, rightCropTransform, rightCropState); + } + float scaleX = containerWidth / (float) bitmapWidth; + float scaleY = containerHeight / (float) bitmapHeight; + float scale = Math.min(scaleX, scaleY); + int width = (int) (bitmapWidth * scale); + int height = (int) (bitmapHeight * scale); + boolean mirror = false; + if (!imagesArrLocals.isEmpty()) { + if (currentEditMode == EDIT_MODE_CROP || sendPhotoType == SELECT_TYPE_AVATAR) { + mirror = rightCropTransform.isMirrored(); + } else { + mirror = rightCropState != null && rightCropState.mirrored; + } + } + if (mirror) { + canvas.scale(-1, 1); + } + + float p = 1.5f; + Bitmap blurBitmap = rightBlur.getBitmap(sideImage); + if (blurBitmap != null) { + drawer.paint.setShader(null); + drawer.paint.setAlpha((int) (0xFF * alpha * maxAlpha * rightMaxAlpha)); + canvas.scale((float) blurBitmap.getWidth() / (blurBitmap.getWidth() - 2 * p), (float) blurBitmap.getHeight() / (blurBitmap.getHeight() - 2 * p)); + canvas.translate(-width / 2, -height / 2); + canvas.scale(1f * width / blurBitmap.getWidth(), 1f * height / blurBitmap.getHeight()); + if (clip) { + int pp = (int) (p - 1.5f); + canvas.clipRect(pp, pp, blurBitmap.getWidth() - pp, blurBitmap.getHeight() - pp); + } + canvas.drawBitmap(blurBitmap, 0, 0, drawer.paint); + } + + canvas.restore(); + } + } + + float translateX = currentTranslationX; + float scaleDiff = 0; + float alpha = 1; + if (!zoomAnimation && translateX > maxX && currentEditMode == EDIT_MODE_NONE && sendPhotoType != SELECT_TYPE_AVATAR) { + alpha = Math.min(1.0f, (translateX - maxX) / containerWidth); + scaleDiff = alpha * 0.3f; + alpha = 1.0f - alpha; + translateX = maxX; + } + boolean drawnCenterImage = false; + boolean drawTextureView = videoSizeSet && aspectRatioFrameLayout != null && aspectRatioFrameLayout.getVisibility() == View.VISIBLE; + if (centerImage.hasBitmapImage() || drawTextureView && textureUploaded) { + canvas.save(); + canvas.translate(containerWidth / 2 + getAdditionX(), containerHeight / 2 + getAdditionY()); + canvas.translate(translateX, currentTranslationY + (currentEditMode != EDIT_MODE_PAINT ? currentPanTranslationY : 0)); + canvas.scale(currentScale - scaleDiff, currentScale - scaleDiff); + canvas.rotate(currentRotation); + if (allowCrossfade) { + imageBoundsMatrix.preTranslate(containerWidth / 2 + getAdditionX(), containerHeight / 2 + getAdditionY()); + imageBoundsMatrix.preTranslate(translateX, currentTranslationY + (currentEditMode != EDIT_MODE_PAINT ? currentPanTranslationY : 0)); + imageBoundsMatrix.preScale(currentScale - scaleDiff, currentScale - scaleDiff, 0, 0); + imageBoundsMatrix.preRotate(currentRotation); + } + if (currentEditMode == EDIT_MODE_PAINT && photoPaintView != null) { + int trueH = getContainerViewHeight(true, 0); + trueH -= photoPaintView.getEmojiPadding(Math.abs(AndroidUtilities.displaySize.y + AndroidUtilities.statusBarHeight - trueH) < dp(20)); + int h = getContainerViewHeight(false, 0); + canvas.translate(0, (trueH - h) / 2f * (1f - photoPaintView.adjustPanLayoutHelperProgress())); + if (allowCrossfade) { + imageBoundsMatrix.preTranslate(0, (trueH - h) / 2f * (1f - photoPaintView.adjustPanLayoutHelperProgress())); + } + } + + int bitmapWidth, originalWidth; + int bitmapHeight, originalHeight; + if (drawTextureView && textureUploaded && videoSizeSet) { + View view = usedSurfaceView ? videoSurfaceView : videoTextureView; + originalWidth = bitmapWidth = view.getMeasuredWidth(); + originalHeight = bitmapHeight = view.getMeasuredHeight(); + } else { + originalWidth = bitmapWidth = centerImage.getBitmapWidth(); + originalHeight = bitmapHeight = centerImage.getBitmapHeight(); + } + + float scale = Math.min(containerWidth / (float) originalWidth, containerHeight / (float) originalHeight); + int width = (int) (originalWidth * scale); + int height = (int) (originalHeight * scale); + float W = width, H = height; + if (!pipAnimationInProgress && (!drawTextureView || !textureUploaded && !videoSizeSet || !videoCrossfadeStarted || videoCrossfadeAlpha != 1.0f)) { + if (!(videoFrameBitmap != null && isCurrentVideo)) { + W = centerImage.getBitmapWidth(); + H = centerImage.getBitmapHeight(); + float S; + if (isCurrentVideo && currentEditMode == EDIT_MODE_NONE && sendPhotoType == SELECT_TYPE_AVATAR) { + S = getCropFillScale(false); + } else { + S = Math.min(containerWidth / W, containerHeight / H); + } + W *= S; + H *= S; + if (isCurrentVideo) { + float centerMaxAlpha = 1f; + if (allowCrossfade) { + imageBoundsMatrix.preTranslate(-W / 2, -H / 2); + imageBoundsPoints[0] = 0; + imageBoundsPoints[1] = 0; + imageBoundsPoints[2] = W; + imageBoundsPoints[3] = 0; + imageBoundsPoints[4] = W; + imageBoundsPoints[5] = H; + imageBoundsPoints[6] = 0; + imageBoundsPoints[7] = H; + imageBoundsMatrix.mapPoints(imageBoundsPoints); + imageBounds.set( + Math.min(Math.min(imageBoundsPoints[0], imageBoundsPoints[2]), Math.min(imageBoundsPoints[4], imageBoundsPoints[6])), + Math.min(Math.min(imageBoundsPoints[1], imageBoundsPoints[3]), Math.min(imageBoundsPoints[5], imageBoundsPoints[7])), + Math.max(Math.max(imageBoundsPoints[0], imageBoundsPoints[2]), Math.max(imageBoundsPoints[4], imageBoundsPoints[6])), + Math.max(Math.max(imageBoundsPoints[1], imageBoundsPoints[3]), Math.max(imageBoundsPoints[5], imageBoundsPoints[7])) + ); + centerMaxAlpha = centerImageInsideBlur[1].set(blurBounds.intersect(imageBounds)); + } + + float p = 1.5f; + Bitmap blurBitmap = null; + if (videoCrossfadeAlpha < 1) { + blurBitmap = centerBlur.getBitmap(centerImage); + if (blurBitmap != null) { + canvas.save(); + drawer.paint.setShader(null); + drawer.paint.setAlpha((int) (0xFF * centerMaxAlpha * alpha * (1f - videoCrossfadeAlpha) * maxAlpha)); + canvas.scale((float) blurBitmap.getWidth() / (blurBitmap.getWidth() - 2 * p), (float) blurBitmap.getHeight() / (blurBitmap.getHeight() - 2 * p)); + canvas.translate(-W / 2, -H / 2); + canvas.scale(1f * W / blurBitmap.getWidth(), 1f * H / blurBitmap.getHeight()); + if (clip) { + int pp = (int) (p - 1.5f); + canvas.clipRect(pp, pp, blurBitmap.getWidth() - pp, blurBitmap.getHeight() - pp); + } + canvas.drawBitmap(blurBitmap, 0, 0, drawer.paint); + canvas.restore(); + } + } + if (videoCrossfadeAlpha > 0) { + blurBitmap = blurManager.getBitmap(); + if (blurBitmap != null) { + canvas.save(); + drawer.paint.setShader(null); + drawer.paint.setAlpha((int) (0xFF * centerMaxAlpha * alpha * videoCrossfadeAlpha * maxAlpha)); + canvas.scale((float) blurBitmap.getWidth() / (blurBitmap.getWidth() - 2 * p), (float) blurBitmap.getHeight() / (blurBitmap.getHeight() - 2 * p)); + canvas.translate(-W / 2, -H / 2); + canvas.scale(1f * W / blurBitmap.getWidth(), 1f * H / blurBitmap.getHeight()); + if (clip) { + int pp = (int) (p - 1.5f); + canvas.clipRect(pp, pp, blurBitmap.getWidth() - pp, blurBitmap.getHeight() - pp); + } + canvas.drawBitmap(blurBitmap, 0, 0, drawer.paint); + canvas.restore(); + } + } + drawnCenterImage = true; + } + } + } + + boolean applyCrop; + float scaleToFitX = 1.0f; + if (!imagesArrLocals.isEmpty()) { + if (currentEditMode == EDIT_MODE_PAINT || switchingToMode == EDIT_MODE_PAINT) { + applyCrop = true; + } else if (sendPhotoType == SELECT_TYPE_AVATAR) { + applyCrop = (switchingToMode == EDIT_MODE_NONE || currentEditMode != EDIT_MODE_PAINT && currentEditMode != EDIT_MODE_FILTER); + } else { + applyCrop = imageMoveAnimation != null && switchingToMode != -1 || currentEditMode == EDIT_MODE_NONE || currentEditMode == EDIT_MODE_CROP || switchingToMode != -1; + } + } else { + applyCrop = false; + } + if (applyCrop) { + int rotatedWidth = originalWidth; + int rotatedHeight = originalHeight; + int orientation = cropTransform.getOrientation(); + if (orientation == 90 || orientation == 270) { + int temp = bitmapWidth; + bitmapWidth = bitmapHeight; + bitmapHeight = temp; + + temp = rotatedWidth; + rotatedWidth = rotatedHeight; + rotatedHeight = temp; + } + float cropAnimationValue; + if (sendPhotoType != SELECT_TYPE_AVATAR && (currentEditMode == EDIT_MODE_PAINT || switchingToMode == EDIT_MODE_PAINT)) { + cropAnimationValue = 1.0f; + } else if (imageMoveAnimation != null && switchingToMode != -1) { + if (currentEditMode == EDIT_MODE_CROP || switchingToMode == EDIT_MODE_CROP || (currentEditMode == EDIT_MODE_FILTER || currentEditMode == EDIT_MODE_PAINT) && switchingToMode == -1) { + cropAnimationValue = 1.0f; + } else if (switchingToMode == EDIT_MODE_NONE) { + cropAnimationValue = animationValue; + } else { + cropAnimationValue = 1.0f - animationValue; + } + } else { + cropAnimationValue = currentEditMode == EDIT_MODE_FILTER || currentEditMode == EDIT_MODE_PAINT ? 0.0f : 1.0f; + } + float cropPw = cropTransform.getCropPw(); + float cropPh = cropTransform.getCropPh(); + bitmapWidth *= cropPw + (1.0f - cropPw) * (1.0f - cropAnimationValue); + bitmapHeight *= cropPh + (1.0f - cropPh) * (1.0f - cropAnimationValue); + scaleToFitX = containerWidth / (float) bitmapWidth; + if (scaleToFitX * bitmapHeight > containerHeight) { + scaleToFitX = containerHeight / (float) bitmapHeight; + } + if (sendPhotoType != SELECT_TYPE_AVATAR && (currentEditMode != 1 || switchingToMode == EDIT_MODE_NONE) && editState.cropState != null) { + float startW = bitmapWidth * scaleToFitX; + float startH = bitmapHeight * scaleToFitX; + float originalScaleToFitX = containerWidth / (float) originalWidth; + if (originalScaleToFitX * originalHeight > containerHeight) { + originalScaleToFitX = containerHeight / (float) originalHeight; + } + float finalW = originalWidth * originalScaleToFitX / (currentScale - scaleDiff); + float finalH = originalHeight * originalScaleToFitX / (currentScale - scaleDiff); + + float w = startW + (finalW - startW) * (1.0f - cropAnimationValue); + float h = startH + (finalH - startH) * (1.0f - cropAnimationValue); + + canvas.clipRect(-w / 2, -h / 2, w / 2, h / 2); + } + if (sendPhotoType == SELECT_TYPE_AVATAR || cropTransform.hasViewTransform()) { + float cropScale; + if (currentEditMode == EDIT_MODE_CROP || sendPhotoType == SELECT_TYPE_AVATAR) { + if (videoTextureView != null) { + videoTextureView.setScaleX(cropTransform.isMirrored() ? -1.0f : 1.0f); + if (firstFrameView != null) { + firstFrameView.setScaleX(videoTextureView.getScaleX()); + } + } + float trueScale = 1.0f + (cropTransform.getTrueCropScale() - 1.0f) * (1.0f - cropAnimationValue); + cropScale = cropTransform.getScale() / trueScale; + float scaleToFit = containerWidth / (float) rotatedWidth; + if (scaleToFit * rotatedHeight > containerHeight) { + scaleToFit = containerHeight / (float) rotatedHeight; + } + cropScale *= scaleToFit / scale; + if (sendPhotoType == SELECT_TYPE_AVATAR) { + if (currentEditMode == EDIT_MODE_PAINT || switchingToMode == EDIT_MODE_PAINT) { + cropScale /= 1.0f + (cropTransform.getMinScale() - 1.0f) * (1.0f - cropAnimationValue); + } else if (switchingToMode == EDIT_MODE_NONE) { + cropScale /= cropTransform.getMinScale(); + } + } + } else { + if (videoTextureView != null) { + videoTextureView.setScaleX(editState.cropState != null && editState.cropState.mirrored ? -1.0f : 1.0f); + if (firstFrameView != null) { + firstFrameView.setScaleX(videoTextureView.getScaleX()); + } + } + cropScale = editState.cropState != null ? editState.cropState.cropScale : 1.0f; + float trueScale = 1.0f + (cropScale - 1.0f) * (1.0f - cropAnimationValue); + cropScale *= scaleToFitX / scale / trueScale; + } + + canvas.translate(cropTransform.getCropAreaX() * cropAnimationValue, cropTransform.getCropAreaY() * cropAnimationValue); + canvas.scale(cropScale, cropScale); + canvas.translate(cropTransform.getCropPx() * rotatedWidth * scale * cropAnimationValue, cropTransform.getCropPy() * rotatedHeight * scale * cropAnimationValue); + if (allowCrossfade) { + imageBoundsMatrix.preTranslate(cropTransform.getCropAreaX() * cropAnimationValue, cropTransform.getCropAreaY() * cropAnimationValue); + imageBoundsMatrix.preScale(cropScale, cropScale); + imageBoundsMatrix.preTranslate(cropTransform.getCropPx() * rotatedWidth * scale * cropAnimationValue, cropTransform.getCropPy() * rotatedHeight * scale * cropAnimationValue); + } + float rotation = (cropTransform.getRotation() + orientation); + if (rotation > 180) { + rotation -= 360; + } + if (sendPhotoType == SELECT_TYPE_AVATAR && (currentEditMode == EDIT_MODE_PAINT || switchingToMode == EDIT_MODE_PAINT)) { + canvas.rotate(rotation); + if (allowCrossfade) { + imageBoundsMatrix.preRotate(rotation); + } + } else { + canvas.rotate(rotation * cropAnimationValue); + if (allowCrossfade) { + imageBoundsMatrix.preRotate(rotation * cropAnimationValue); + } + } + } + } + + if (!drawnCenterImage) { + boolean mirror = false; + if (!imagesArrLocals.isEmpty()) { + if (currentEditMode == EDIT_MODE_CROP || sendPhotoType == SELECT_TYPE_AVATAR) { + mirror = cropTransform.isMirrored(); + } else { + mirror = editState.cropState != null && editState.cropState.mirrored; + } + } + boolean restore = false; + if (mirror) { + canvas.save(); + canvas.scale(-1, 1); + restore = true; + } + if (currentMirror > 0) { + if (!restore) { + canvas.save(); + restore = true; + } + canvas.scale(1 - currentMirror * 2, 1f); + canvas.skew(0, 4 * currentMirror * (1f - currentMirror) * .25f); + } + if (photoViewerWebView == null || !photoViewerWebView.isLoaded()) { + float centerMaxAlpha = 1f; + if (allowCrossfade) { + imageBoundsMatrix.preTranslate(-W / 2, -H / 2); + imageBoundsPoints[0] = 0; + imageBoundsPoints[1] = 0; + imageBoundsPoints[2] = W; + imageBoundsPoints[3] = 0; + imageBoundsPoints[4] = W; + imageBoundsPoints[5] = H; + imageBoundsPoints[6] = 0; + imageBoundsPoints[7] = H; + imageBoundsMatrix.mapPoints(imageBoundsPoints); + imageBounds.set( + Math.min(Math.min(imageBoundsPoints[0], imageBoundsPoints[2]), Math.min(imageBoundsPoints[4], imageBoundsPoints[6])), + Math.min(Math.min(imageBoundsPoints[1], imageBoundsPoints[3]), Math.min(imageBoundsPoints[5], imageBoundsPoints[7])), + Math.max(Math.max(imageBoundsPoints[0], imageBoundsPoints[2]), Math.max(imageBoundsPoints[4], imageBoundsPoints[6])), + Math.max(Math.max(imageBoundsPoints[1], imageBoundsPoints[3]), Math.max(imageBoundsPoints[5], imageBoundsPoints[7])) + ); + centerMaxAlpha = centerImageInsideBlur[1].set(blurBounds.intersect(imageBounds)); + } + + float p = 1.5f; + Bitmap blurBitmap = blurManager.getBitmap(); + if (blurBitmap == null) { + blurBitmap = centerBlur.getBitmap(centerImage); + } + if (blurBitmap != null) { + drawer.paint.setShader(null); + drawer.paint.setAlpha((int) (0xFF * alpha * centerMaxAlpha * maxAlpha)); + canvas.scale((float) blurBitmap.getWidth() / (blurBitmap.getWidth() - 2 * p), (float) blurBitmap.getHeight() / (blurBitmap.getHeight() - 2 * p)); + canvas.translate(-W / 2, -H / 2); + canvas.scale(1f * W / blurBitmap.getWidth(), 1f * H / blurBitmap.getHeight()); + if (clip) { + int pp = (int) (p - 1.5f); + canvas.clipRect(pp, pp, blurBitmap.getWidth() - pp, blurBitmap.getHeight() - pp); + } + canvas.drawBitmap(blurBitmap, 0, 0, drawer.paint); + } + } + if (restore) { + canvas.restore(); + } + drawnCenterImage = true; + } + canvas.restore(); + } + if (!drawnCenterImage && animatingImageView.getVisibility() == View.VISIBLE) { + canvas.save(); + if (padImageForHorizontalInsets) { + canvas.translate(getRightInset() / 2 - getLeftInset() / 2, 0); + } + canvas.translate(animatingImageView.getX(), animatingImageView.getY()); + canvas.scale(animatingImageView.getScaleX(), animatingImageView.getScaleY(), animatingImageView.getPivotX(), animatingImageView.getPivotY()); + + float p = 1.5f; + Bitmap blurBitmap = centerBlur.getBitmap(animatingImageView.getBitmapHolder()); + if (blurBitmap != null) { + canvas.save(); + drawer.paint.setShader(null); + drawer.paint.setAlpha((int) (0xFF * maxAlpha)); + canvas.scale(1f * animatingImageView.getWidth() / blurBitmap.getWidth(), 1f * animatingImageView.getHeight() / blurBitmap.getHeight()); + canvas.scale((float) blurBitmap.getWidth() / (blurBitmap.getWidth() - 2 * p), (float) blurBitmap.getHeight() / (blurBitmap.getHeight() - 2 * p), blurBitmap.getWidth() / 2f, blurBitmap.getHeight() / 2f); + canvas.drawBitmap(blurBitmap, 0, 0, drawer.paint); + if (clip) { + int pp = (int) (p - 1.5f); + canvas.clipRect(pp, pp, blurBitmap.getWidth() - pp, blurBitmap.getHeight() - pp); + } + canvas.restore(); + } + + canvas.restore(); + drawnCenterImage = true; + } + + if (sideImage == leftImage) { + float leftMaxAlpha = allowCrossfade ? centerImageInsideBlur[0].set(1f, true) : 1f; + if (sideImage != null && sideImage.hasBitmapImage()) { + canvas.save(); + canvas.translate(containerWidth / 2, containerHeight / 2); + canvas.translate(-(containerWidth * (scale + 1) + dp(30)) / 2 + currentTranslationX, 0); + int bitmapWidth = sideImage.getBitmapWidth(); + int bitmapHeight = sideImage.getBitmapHeight(); + if (!leftImageIsVideo && leftCropState != null && leftCropTransform.hasViewTransform()) { + applyCrop(canvas, containerWidth, containerHeight, bitmapWidth, bitmapHeight, currentScale, leftCropTransform, leftCropState); + } + float scaleX = containerWidth / (float) bitmapWidth; + float scaleY = containerHeight / (float) bitmapHeight; + float scale = Math.min(scaleX, scaleY); + int width = (int) (bitmapWidth * scale); + int height = (int) (bitmapHeight * scale); + + boolean mirror = false; + if (!imagesArrLocals.isEmpty()) { + if (currentEditMode == EDIT_MODE_CROP || sendPhotoType == SELECT_TYPE_AVATAR) { + mirror = leftCropTransform.isMirrored(); + } else { + mirror = leftCropState != null && leftCropState.mirrored; + } + } + if (mirror) { + canvas.scale(-1, 1); + } + + Bitmap blurBitmap = leftBlur.getBitmap(sideImage); + float p = 1.5f; + if (blurBitmap != null) { + drawer.paint.setShader(null); + drawer.paint.setAlpha((int) (0xFF * maxAlpha * leftMaxAlpha)); + canvas.scale((float) blurBitmap.getWidth() / (blurBitmap.getWidth() - 2 * p), (float) blurBitmap.getHeight() / (blurBitmap.getHeight() - 2 * p)); + canvas.translate(-width / 2, -height / 2); + canvas.scale(1f * width / (blurBitmap.getWidth()), 1f * height / (blurBitmap.getHeight())); + if (clip) { + int pp = (int) (p - 1.5f); + canvas.clipRect(pp, pp, blurBitmap.getWidth() - pp, blurBitmap.getHeight() - pp); + } + canvas.drawBitmap(blurBitmap, 0, 0, drawer.paint); + } + + canvas.restore(); + } + } + + canvas.restoreToCount(restoreCount); + + if (overlayColor != 0) { + drawer.paint.setColor(overlayColor); + drawer.paint.setAlpha((int) (drawer.paint.getAlpha() * AndroidUtilities.lerp(.7f, 1f, maxAlpha))); + canvas.drawPaint(drawer.paint); + } + } + + private void invalidateBlur() { + if (animationInProgress != 0) { + return; + } + if (captionEdit != null) { + captionEdit.invalidateBlur(); + } + if (videoTimelineView != null) { + videoTimelineView.invalidateBlur(); + } + if (containerView != null) { + containerView.invalidate(); + } } - 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 2e2555b0ed..ce7568c823 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PinchToZoomHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PinchToZoomHelper.java @@ -8,6 +8,8 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.Path; @@ -43,6 +45,7 @@ import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.spoilers.SpoilerEffect; +import org.telegram.ui.Components.spoilers.SpoilerEffect2; public class PinchToZoomHelper { @@ -59,6 +62,7 @@ public class PinchToZoomHelper { private ImageReceiver blurImage = new ImageReceiver(); private boolean hasMediaSpoiler; private SpoilerEffect mediaSpoilerEffect = new SpoilerEffect(); + private SpoilerEffect2 mediaSpoilerEffect2; private Path path = new Path(); private float[] spoilerRadii = new float[8]; @@ -117,7 +121,7 @@ public PinchToZoomHelper() { this.isSimple = true; } - public void startZoom(View child, ImageReceiver image, TextureView textureView, MessageObject messageObject) { + public void startZoom(View child, ImageReceiver image, TextureView textureView, MessageObject messageObject, int spoilerEffect2AttachIndex) { this.child = child; this.messageObject = messageObject; @@ -148,6 +152,14 @@ public void startZoom(View child, ImageReceiver image, TextureView textureView, parentView.addView(overlayView); hasMediaSpoiler = messageObject != null && messageObject.hasMediaSpoilers() && !messageObject.isMediaSpoilersRevealed; + if (hasMediaSpoiler) { + if (mediaSpoilerEffect2 == null && SpoilerEffect2.supports()) { + mediaSpoilerEffect2 = SpoilerEffect2.getInstance(overlayView); + if (mediaSpoilerEffect2 != null) { + mediaSpoilerEffect2.reassignAttach(overlayView, spoilerEffect2AttachIndex); + } + } + } if (blurImage.getBitmap() != null) { blurImage.getBitmap().recycle(); blurImage.setImageBitmap((Bitmap) null); @@ -155,6 +167,9 @@ public void startZoom(View child, ImageReceiver image, TextureView textureView, if (image.getBitmap() != null && !image.getBitmap().isRecycled() && hasMediaSpoiler) { blurImage.setImageBitmap(Utilities.stackBlurBitmapMax(image.getBitmap())); + blurImage.setColorFilter(getFancyBlurFilter()); + } else { + blurImage.setColorFilter(null); } setFullImage(messageObject); @@ -319,6 +334,10 @@ public void clear() { if (overlayView != null && overlayView.getParent() != null) { parentView.removeView(overlayView); overlayView.backupImageView.getImageReceiver().clearImage(); + if (mediaSpoilerEffect2 != null) { + mediaSpoilerEffect2.detach(overlayView); + mediaSpoilerEffect2 = null; + } if (childImage != null) { Drawable drawable = this.childImage.getDrawable(); @@ -621,10 +640,15 @@ private void drawImage(Canvas canvas) { canvas.save(); canvas.clipPath(path); - int sColor = Color.WHITE; - mediaSpoilerEffect.setColor(ColorUtils.setAlphaComponent(sColor, (int) (Color.alpha(sColor) * 0.325f * childImage.getAlpha()))); - mediaSpoilerEffect.setBounds((int) childImage.getImageX(), (int) childImage.getImageY(), (int) childImage.getImageX2(), (int) childImage.getImageY2()); - mediaSpoilerEffect.draw(canvas); + if (mediaSpoilerEffect2 != null) { + canvas.translate(childImage.getImageX(), childImage.getImageY()); + mediaSpoilerEffect2.draw(canvas, overlayView, (int) childImage.getImageWidth(), (int) childImage.getImageHeight()); + } else { + int sColor = Color.WHITE; + mediaSpoilerEffect.setColor(ColorUtils.setAlphaComponent(sColor, (int) (Color.alpha(sColor) * 0.325f * childImage.getAlpha()))); + mediaSpoilerEffect.setBounds((int) childImage.getImageX(), (int) childImage.getImageY(), (int) childImage.getImageX2(), (int) childImage.getImageY2()); + mediaSpoilerEffect.draw(canvas); + } canvas.restore(); invalidate(); @@ -717,6 +741,10 @@ public interface ClipBoundsListener { } public boolean checkPinchToZoom(MotionEvent ev, View child, ImageReceiver image, TextureView textureView, MessageObject messageObject) { + return checkPinchToZoom(ev, child, image, textureView, messageObject, 0); + } + + public boolean checkPinchToZoom(MotionEvent ev, View child, ImageReceiver image, TextureView textureView, MessageObject messageObject, int spoilerEffect2Index) { if (!zoomEnabled(child, image)) { return false; } @@ -757,7 +785,7 @@ public boolean checkPinchToZoom(MotionEvent ev, View child, ImageReceiver image, pinchTranslationX = 0f; pinchTranslationY = 0f; child.getParent().requestDisallowInterceptTouchEvent(true); - startZoom(child, image, textureView, messageObject); + startZoom(child, image, textureView, messageObject, spoilerEffect2Index); } @@ -815,4 +843,15 @@ public void applyTransform(Canvas canvas) { public View getChild() { return child; } + + private ColorMatrixColorFilter fancyBlurFilter; + private ColorMatrixColorFilter getFancyBlurFilter() { + if (fancyBlurFilter == null) { + ColorMatrix colorMatrix = new ColorMatrix(); + AndroidUtilities.multiplyBrightnessColorMatrix(colorMatrix, .9f); + AndroidUtilities.adjustSaturationColorMatrix(colorMatrix, +.6f); + fancyBlurFilter = new ColorMatrixColorFilter(colorMatrix); + } + return fancyBlurFilter; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PopupNotificationActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PopupNotificationActivity.java index ceb48353e2..9702fffe25 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PopupNotificationActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PopupNotificationActivity.java @@ -164,7 +164,7 @@ protected void onCreate(Bundle savedInstanceState) { Theme.createDialogsResources(this); Theme.createChatResources(this, false); - AndroidUtilities.fillStatusBarHeight(this); + AndroidUtilities.fillStatusBarHeight(this, false); for (int a : SharedConfig.activeAccounts) { NotificationCenter.getInstance(a).addObserver(this, NotificationCenter.appDidLogout); NotificationCenter.getInstance(a).addObserver(this, NotificationCenter.updateInterfaces); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PremiumPreviewFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/PremiumPreviewFragment.java index 67b0493ee7..fa9ff843cc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PremiumPreviewFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PremiumPreviewFragment.java @@ -663,7 +663,7 @@ public static void fillPremiumFeaturesList(ArrayList premium }); } - private static CharSequence applyNewSpan(String str) { + public static CharSequence applyNewSpan(String str) { SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(str); spannableStringBuilder.append(" d"); FilterCreateActivity.NewSpan span = new FilterCreateActivity.NewSpan(false); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java index 32a464abb1..4b04596d04 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java @@ -202,6 +202,7 @@ import org.telegram.ui.Components.ItemOptions; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LinkSpanDrawable; +import org.telegram.ui.Components.MediaActivity; import org.telegram.ui.Components.Paint.PersistColorPalette; import org.telegram.ui.Components.Premium.GiftPremiumBottomSheet; import org.telegram.ui.Components.Premium.PremiumFeatureBottomSheet; @@ -487,6 +488,7 @@ public void setAlpha(int a) { private final static int add_photo = 36; private final static int qr_button = 37; // private final static int gift_premium = 38; + private final static int channel_stories = 39; private Rect rect = new Rect(); @@ -700,8 +702,10 @@ public static class AvatarImageView extends BackupImageView { private float foregroundAlpha; private ImageReceiver.BitmapHolder drawableHolder; boolean drawForeground = true; + float progressToExpand; ProfileGalleryView avatarsViewPager; + private boolean hasStories; public void setAvatarsViewPager(ProfileGalleryView avatarsViewPager) { this.avatarsViewPager = avatarsViewPager; @@ -781,7 +785,9 @@ public void setRoundRadius(int value) { protected void onDraw(Canvas canvas) { ImageReceiver imageReceiver = animatedEmojiDrawable != null ? animatedEmojiDrawable.getImageReceiver() : this.imageReceiver; if (imageReceiver != null && (foregroundAlpha < 1f || !drawForeground)) { - imageReceiver.setImageCoords(0, 0, getMeasuredWidth(), getMeasuredHeight()); + int inset = hasStories ? (int) AndroidUtilities.dpf2(3.5f) : 0; + inset *= (1f - progressToExpand); + imageReceiver.setImageCoords(inset, inset, getMeasuredWidth() - inset * 2f, getMeasuredHeight() - inset * 2f); imageReceiver.draw(canvas); } if (foregroundAlpha > 0f && drawForeground) { @@ -809,6 +815,26 @@ public void invalidate() { public void drawForeground(boolean drawForeground) { this.drawForeground = drawForeground; } + + public ChatActivityInterface getPrevFragment() { + return null; + } + + public void setHasStories(boolean hasStories) { + if (this.hasStories == hasStories) { + return; + } + this.hasStories = hasStories; + invalidate(); + } + + public void setProgressToExpand(float animatedFracture) { + if (progressToExpand == animatedFracture) { + return; + } + progressToExpand = animatedFracture; + invalidate(); + } } private class TopView extends View { @@ -2223,7 +2249,14 @@ public void didChangeOwner(TLRPC.User user) { openDiscussion(); } /*else if (id == gift_premium) { showDialog(new GiftPremiumBottomSheet(ProfileActivity.this, getMessagesController().getUser(userId))); - } */ else if (id == start_secret_chat) { + } */ else if (id == channel_stories) { + Bundle args = new Bundle(); + args.putInt("type", MediaActivity.TYPE_ARCHIVED_CHANNEL_STORIES); + args.putLong("dialog_id", -chatId); + MediaActivity fragment = new MediaActivity(args, null); + fragment.setChatInfo(chatInfo); + presentFragment(fragment); + } else if (id == start_secret_chat) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity(), resourcesProvider); builder.setTitle(LocaleController.getString("AreYouSureSecretChatTitle", R.string.AreYouSureSecretChatTitle)); builder.setMessage(LocaleController.getString("AreYouSureSecretChat", R.string.AreYouSureSecretChat)); @@ -3762,6 +3795,7 @@ public boolean onItemClick(View view, int position) { 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, + BuildVars.DEBUG_PRIVATE_VERSION ? (SharedConfig.photoViewerBlur ? "do not blur in photoviewer" : "blur in photoviewer") : null }; builder.setItems(items, (dialog, which) -> { @@ -3786,7 +3820,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").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").remove("askNotificationsAfter").remove("askNotificationsDuration").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").remove("askNotificationsAfter").remove("askNotificationsDuration").remove("viewoncehint").commit(); MessagesController.getEmojiSettings(currentAccount).edit().remove("featured_hidden").remove("emoji_featured_hidden").commit(); SharedConfig.textSelectionHintShows = 0; SharedConfig.lockRecordAudioVideoHint = 0; @@ -4033,6 +4067,8 @@ protected void onSend(LongSparseArray dids, int count, TLRPC.TL_fo for (int i = 0; i < getParentLayout().getFragmentStack().size(); i++) { getParentLayout().getFragmentStack().get(i).storyViewer = null; } + } else if (which == 24) { + SharedConfig.togglePhotoViewerBlur(); } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); @@ -4356,6 +4392,7 @@ protected void dispatchDraw(Canvas canvas) { } openAvatar(); }); + avatarImage.setHasStories(needInsetForStories()); avatarImage.setOnLongClickListener(v -> { if (avatarBig != null || isTopic) { return false; @@ -4563,11 +4600,12 @@ 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) { + storyView = new ProfileStoriesView(context, currentAccount, userId == 0 ? chatId : userId, avatarContainer, avatarImage, resourcesProvider) { @Override protected void onTap(StoryViewer.PlaceProvider provider) { - if (getMessagesController().getStoriesController().hasStories(userId)) { - getOrCreateStoryViewer().open(context, userId, provider); + long did = userId == 0 ? chatId : userId; + if (getMessagesController().getStoriesController().hasStories(did)) { + getOrCreateStoryViewer().open(context, did, provider); } else if (userInfo != null && userInfo.stories != null && !userInfo.stories.stories.isEmpty() && userId != getUserConfig().clientUserId) { getOrCreateStoryViewer().open(context, userInfo.stories, provider); } else { @@ -6783,6 +6821,9 @@ public void didReceivedNotification(int id, int account, final Object... args) { } chatInfo = chatFull; } + if (sharedMediaLayout != null) { + sharedMediaLayout.setChatInfo(chatInfo); + } if (chatInfo != null && (chatInfo.call == null && !hasVoiceChatItem || chatInfo.call != null && hasVoiceChatItem)) { createActionBarMenu(false); } @@ -7160,6 +7201,10 @@ public void setAvatarAnimationProgress(float progress) { avatarAnimationProgress = currentExpandAnimatorValue = progress; checkPhotoDescriptionAlpha(); + if (playProfileAnimation == 2) { + avatarImage.setProgressToExpand(progress); + } + listView.setAlpha(progress); listView.setTranslationX(AndroidUtilities.dp(48) - AndroidUtilities.dp(48) * progress); @@ -7310,8 +7355,10 @@ public AnimatorSet onCustomTransitionAnimation(final boolean isOpen, final Runna 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)); + storyView.setAlpha(1f); + if (dialogId > 0) { + animators.add(ObjectAnimator.ofFloat(storyView, View.ALPHA, 1.0f)); + } } if (timeItem.getTag() != null) { animators.add(ObjectAnimator.ofFloat(timeItem, View.ALPHA, 1.0f, 0.0f)); @@ -7377,7 +7424,9 @@ public AnimatorSet onCustomTransitionAnimation(final boolean isOpen, final Runna 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 (dialogId > 0) { + animators.add(ObjectAnimator.ofFloat(storyView, View.ALPHA, 0.0f)); + } } if (timeItem.getTag() != null) { timeItem.setAlpha(0f); @@ -7444,6 +7493,7 @@ public void onAnimationEnd(Animator animation) { callback.run(); return; } + avatarImage.setProgressToExpand(0); listView.setLayerType(View.LAYER_TYPE_NONE, null); if (animatingItem != null) { ActionBarMenu menu = actionBar.createMenu(); @@ -7539,6 +7589,10 @@ public void setChatInfo(TLRPC.ChatFull value) { fetchUsersFromChannelInfo(); } + private boolean needInsetForStories() { + return getDialogId() < 0 && getMessagesController().getStoriesController().hasStories(getDialogId()); + } + public void setUserInfo(TLRPC.UserFull value) { userInfo = value; if (storyView != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/QrActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/QrActivity.java index ba5e90281b..a4262cfe57 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/QrActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/QrActivity.java @@ -132,7 +132,7 @@ public class QrActivity extends BaseFragment { } private final ThemeResourcesProvider resourcesProvider = new ThemeResourcesProvider(); - private final EmojiThemes homeTheme = EmojiThemes.createHomeQrTheme(); + private final EmojiThemes homeTheme = EmojiThemes.createHomeQrTheme(currentAccount); private final Rect logoRect = new Rect(); private final ArrayMap emojiThemeDarkIcons = new ArrayMap<>(); private int[] prevQrColors = null; @@ -388,7 +388,7 @@ protected void setDarkTheme(boolean isDark) { fragmentView.postDelayed(() -> { firstOpen = false; if (cachedThemes == null || cachedThemes.isEmpty()) { - ChatThemeController.requestAllChatThemes(new ResultCallback>() { + ChatThemeController.getInstance(currentAccount).requestAllChatThemes(new ResultCallback>() { @Override public void onComplete(List result) { onDataLoaded(result); @@ -1467,10 +1467,11 @@ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { } public void onCreate() { - ChatThemeController.preloadAllWallpaperThumbs(true); - ChatThemeController.preloadAllWallpaperThumbs(false); - ChatThemeController.preloadAllWallpaperImages(true); - ChatThemeController.preloadAllWallpaperImages(false); + ChatThemeController chatThemeController = ChatThemeController.getInstance(currentAccount); + chatThemeController.preloadAllWallpaperThumbs(true); + chatThemeController.preloadAllWallpaperThumbs(false); + chatThemeController.preloadAllWallpaperImages(true); + chatThemeController.preloadAllWallpaperImages(false); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java index fefc794ebe..58cf1aec5c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java @@ -8,6 +8,9 @@ package org.telegram.ui; +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.AnimatorSet; @@ -18,9 +21,13 @@ 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.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; import android.graphics.RectF; import android.graphics.SurfaceTexture; import android.graphics.drawable.BitmapDrawable; @@ -28,18 +35,45 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; +import androidx.annotation.Keep; +import androidx.annotation.NonNull; +import androidx.dynamicanimation.animation.FloatValueHolder; +import androidx.dynamicanimation.animation.SpringAnimation; +import androidx.dynamicanimation.animation.SpringForce; + +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 android.text.style.ClickableSpan; +import android.text.style.URLSpan; +import android.transition.ChangeBounds; +import android.transition.Fade; +import android.transition.Transition; +import android.transition.TransitionManager; +import android.transition.TransitionSet; +import android.transition.TransitionValues; +import android.util.Log; +import android.util.Property; import android.util.SparseArray; import android.view.GestureDetector; import android.view.Gravity; +import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.TextureView; import android.view.VelocityTracker; import android.view.View; +import android.view.ViewGroup; import android.view.Window; import android.view.WindowInsets; import android.view.WindowManager; import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; import androidx.annotation.Keep; @@ -50,27 +84,51 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; +import org.telegram.messenger.ChatObject; +import org.telegram.messenger.DialogObject; +import org.telegram.messenger.Emoji; import org.telegram.messenger.ImageLocation; import org.telegram.messenger.ImageReceiver; import org.telegram.messenger.LocaleController; 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; +import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.ActionBar; +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.Components.AlertsCreator; +import org.telegram.ui.Components.AnimatedEmojiDrawable; +import org.telegram.ui.Components.AnimatedEmojiSpan; import org.telegram.ui.Components.AnimationProperties; +import org.telegram.ui.Components.BulletinFactory; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.PlayPauseDrawable; +import org.telegram.ui.Components.RLottieDrawable; +import org.telegram.ui.Components.RadialProgressView; import org.telegram.ui.Components.Scroller; import org.telegram.ui.Components.TimerParticles; +import org.telegram.ui.Components.TranslateAlert2; +import org.telegram.ui.Components.URLSpanReplacement; import org.telegram.ui.Components.VideoPlayer; +import org.telegram.ui.Components.VideoPlayerSeekBar; +import org.telegram.ui.Stories.DarkThemeResourceProvider; +import org.telegram.ui.Stories.recorder.HintView2; import java.io.File; +import java.net.URLEncoder; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Locale; import tw.nekomimi.nekogram.NekoXConfig; @@ -120,18 +178,23 @@ private class SecretDeleteTimer extends FrameLayout { private RectF deleteProgressRect = new RectF(); private TimerParticles timerParticles = new TimerParticles(); + private boolean once; private long destroyTime; private long destroyTtl; private boolean useVideoProgress; - private Drawable drawable; + private RLottieDrawable drawable; + + private TextPaint oncePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private StaticLayout onceLayout; + private float onceLayoutWidth, onceLayoutHeight; public SecretDeleteTimer(Context context) { super(context); setWillNotDraw(false); particlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - particlePaint.setStrokeWidth(AndroidUtilities.dp(1.5f)); + particlePaint.setStrokeWidth(dp(1.5f)); particlePaint.setColor(0xffe6e6e6); particlePaint.setStrokeCap(Paint.Cap.ROUND); particlePaint.setStyle(Paint.Style.STROKE); @@ -140,37 +203,55 @@ public SecretDeleteTimer(Context context) { afterDeleteProgressPaint.setStyle(Paint.Style.STROKE); afterDeleteProgressPaint.setStrokeCap(Paint.Cap.ROUND); afterDeleteProgressPaint.setColor(0xffe6e6e6); - afterDeleteProgressPaint.setStrokeWidth(AndroidUtilities.dp(2)); + afterDeleteProgressPaint.setStrokeWidth(dp(2)); circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); circlePaint.setColor(0x7f000000); - drawable = context.getResources().getDrawable(R.drawable.flame_small); + drawable = new RLottieDrawable(R.raw.fire_on, "" + R.raw.fire_on, dp(16), dp(16)); + drawable.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); + drawable.setMasterParent(this); + drawable.start(); } private void setDestroyTime(long time, long ttl, boolean videoProgress) { + once = false; destroyTime = time; destroyTtl = ttl; useVideoProgress = videoProgress; + drawable.start(); + invalidate(); + } + + private void setOnce() { + once = true; + oncePaint.setTextSize(dp(13)); + oncePaint.setTypeface(AndroidUtilities.getTypeface("fonts/num.otf")); + oncePaint.setColor(Color.WHITE); + onceLayout = new StaticLayout("1", oncePaint, 999, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false); + onceLayoutWidth = onceLayout.getLineCount() > 0 ? onceLayout.getLineWidth(0) : 0; + onceLayoutHeight = onceLayout.getHeight(); invalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - int y = getMeasuredHeight() / 2 - AndroidUtilities.dp(28) / 2; - deleteProgressRect.set(getMeasuredWidth() - AndroidUtilities.dp(30 + 19), y, getMeasuredWidth() - AndroidUtilities.dp(2 + 19), y + AndroidUtilities.dp(28)); + final float cx = getMeasuredWidth() - dp(35); + final float cy = getMeasuredHeight() / 2f; + final float r = dpf2(10.5f); + deleteProgressRect.set(cx - r, cy - r, cx + r, cy + r); + setPivotX(cx); + setPivotY(cy); } @SuppressLint("DrawAllocation") @Override protected void onDraw(Canvas canvas) { - if (currentMessageObject == null || currentMessageObject.messageOwner.destroyTime == 0) { + if (currentMessageObject == null || currentMessageObject.messageOwner.destroyTime == 0 && currentMessageObject.messageOwner.ttl != 0x7FFFFFFF) { return; } - canvas.drawCircle(getMeasuredWidth() - AndroidUtilities.dp(16 + 19), getMeasuredHeight() / 2, AndroidUtilities.dp(16), circlePaint); - float progress; if (useVideoProgress) { @@ -190,14 +271,34 @@ protected void onDraw(Canvas canvas) { progress = Math.max(0, destroyTime - msTime) / (destroyTtl * 1000.0f); } - int x = getMeasuredWidth() - AndroidUtilities.dp(32 - 11 + 19); - int y = (getMeasuredHeight() - AndroidUtilities.dp(14)) / 2 - AndroidUtilities.dp(0.5f); - drawable.setBounds(x, y, x + AndroidUtilities.dp(10), y + AndroidUtilities.dp(14)); - drawable.draw(canvas); - float radProgress = -360 * progress; - canvas.drawArc(deleteProgressRect, -90, radProgress, false, afterDeleteProgressPaint); - - timerParticles.draw(canvas, particlePaint, deleteProgressRect, radProgress, 1.0f); + if (once) { + canvas.save(); + canvas.translate(deleteProgressRect.centerX() - onceLayoutWidth / 2f, deleteProgressRect.centerY() - onceLayoutHeight / 2f); + onceLayout.draw(canvas); + canvas.restore(); + + canvas.drawArc(deleteProgressRect, 90, 180, false, afterDeleteProgressPaint); + final int dashes = 5; + final int gaps = dashes + 1; + final float dashWeight = 1f, gapWeight = 1.5f; + final float dashSweep = dashWeight / (dashes * dashWeight + gaps * gapWeight) * 180; + final float gapSweep = gapWeight / (dashes * dashWeight + gaps * gapWeight) * 180; + float a = gapSweep; + for (int i = 0; i < dashes; ++i) { + canvas.drawArc(deleteProgressRect, 270 + a, dashSweep, false, afterDeleteProgressPaint); + a += dashSweep + gapSweep; + } + timerParticles.draw(canvas, particlePaint, deleteProgressRect, 0, 1.0f); + } else { + final float cx = deleteProgressRect.centerX(); + final float cy = deleteProgressRect.centerY() - dp(1); + final float r = dp(8); + drawable.setBounds((int) (cx - r), (int) (cy - r), (int) (cx + r), (int) (cy + r)); + drawable.draw(canvas); + float radProgress = -360 * progress; + canvas.drawArc(deleteProgressRect, -90, radProgress, false, afterDeleteProgressPaint); + timerParticles.draw(canvas, particlePaint, deleteProgressRect, radProgress, 1.0f); + } invalidate(); } } @@ -233,6 +334,17 @@ public void draw(Canvas canvas) { frame++; } } + + @Override + public void setBounds(int left, int top, int right, int bottom) { + super.setBounds(left, top, right, bottom + AndroidUtilities.navigationBarHeight); + } + + @Override + public void setBounds(@NonNull Rect bounds) { + bounds.bottom += AndroidUtilities.navigationBarHeight; + super.setBounds(bounds); + } } private int currentAccount; @@ -240,8 +352,10 @@ public void draw(Canvas canvas) { private WindowManager.LayoutParams windowLayoutParams; private FrameLayout windowView; private FrameLayoutDrawer containerView; + private View navigationBar; private ImageReceiver centerImage = new ImageReceiver(); private SecretDeleteTimer secretDeleteTimer; + private HintView2 secretHint; private boolean isVisible; private long currentDialogId; private AspectRatioFrameLayout aspectRatioFrameLayout; @@ -257,6 +371,20 @@ public void draw(Canvas canvas) { private long closeTime; private boolean disableShowCheck; private PhotoViewer.PhotoViewerProvider currentProvider; + private int videoWidth, videoHeight; + + private VideoPlayerSeekBar seekbar; + private View seekbarView; + private SimpleTextView videoPlayerTime; + private View seekbarBackground; + private VideoPlayerControlFrameLayout seekbarContainer; + private ImageView playButton; + private PlayPauseDrawable playButtonDrawable; + + private FrameLayout captionContainer; + private TextSelectionHelper.SimpleTextSelectionHelper textSelectionHelper; + private PhotoViewer.CaptionTextViewSwitcher captionTextViewSwitcher; + private PhotoViewer.CaptionScrollView captionScrollView; private int playerRetryPlayCount; @@ -420,7 +548,7 @@ private void preparePlayer(File file) { releasePlayer(); if (videoTextureView == null) { aspectRatioFrameLayout = new AspectRatioFrameLayout(parentActivity); - aspectRatioFrameLayout.setVisibility(View.INVISIBLE); + aspectRatioFrameLayout.setVisibility(View.VISIBLE); containerView.addView(aspectRatioFrameLayout, 0, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.CENTER)); videoTextureView = new TextureView(parentActivity); @@ -429,9 +557,28 @@ private void preparePlayer(File file) { } textureUploaded = false; videoCrossfadeStarted = false; - videoTextureView.setAlpha(videoCrossfadeAlpha = 0.0f); +// videoTextureView.setAlpha(videoCrossfadeAlpha = 0.0f); + videoTextureView.setAlpha(1f); if (videoPlayer == null) { - videoPlayer = new VideoPlayer(); + videoPlayer = new VideoPlayer() { + @Override + public void setPlayWhenReady(boolean playWhenReady) { + super.setPlayWhenReady(playWhenReady); + playButtonDrawable.setPause(playWhenReady); + } + + @Override + public void play() { + super.play(); + playButtonDrawable.setPause(true); + } + + @Override + public void pause() { + super.pause(); + playButtonDrawable.setPause(false); + } + }; videoPlayer.setTextureView(videoTextureView); videoPlayer.setDelegate(new VideoPlayer.VideoPlayerDelegate() { @Override @@ -439,6 +586,8 @@ public void onStateChanged(boolean playWhenReady, int playbackState) { if (videoPlayer == null || currentMessageObject == null) { return; } + AndroidUtilities.cancelRunOnUIThread(updateProgressRunnable); + AndroidUtilities.runOnUIThread(updateProgressRunnable); if (playbackState != ExoPlayer.STATE_ENDED && playbackState != ExoPlayer.STATE_IDLE) { try { parentActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); @@ -464,7 +613,7 @@ public void onStateChanged(boolean playWhenReady, int playbackState) { if (playbackState == ExoPlayer.STATE_ENDED) { videoWatchedOneTime = true; if (closeVideoAfterWatch) { - closePhoto(true, true); + closePhoto(true, !ignoreDelete); } else { videoPlayer.seekTo(0); videoPlayer.play(); @@ -516,8 +665,60 @@ public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { } videoPlayer.preparePlayer(Uri.fromFile(file), "other"); videoPlayer.setPlayWhenReady(true); + playButtonDrawable.setPause(true); } + private final Runnable updateProgressRunnable = () -> { + if (videoPlayer == null) { + return; + } + + long pos = videoPlayer.getCurrentPosition(); + long duration = videoPlayer.getDuration(); + + if (duration == C.TIME_UNSET) { + pos = duration = 0; + } + if (duration > 0 && !seekbar.isDragging()) { + seekbar.setProgress(pos / (float) duration); + seekbarView.invalidate(); + } + updateVideoPlayerTime(); + + if (videoPlayer.isPlaying()) { + AndroidUtilities.runOnUIThread(this.updateProgressRunnable, 17); + } + }; + + private final int[] videoPlayerCurrentTime = new int[2]; + private final int[] videoPlayerTotalTime = new int[2]; + private void updateVideoPlayerTime() { + Arrays.fill(videoPlayerCurrentTime, 0); + Arrays.fill(videoPlayerTotalTime, 0); + if (videoPlayer != null) { + long current = Math.max(0, videoPlayer.getCurrentPosition()); + long total = Math.max(0, videoPlayer.getDuration()); + current /= 1000; + total /= 1000; + videoPlayerCurrentTime[0] = (int) (current / 60); + videoPlayerCurrentTime[1] = (int) (current % 60); + videoPlayerTotalTime[0] = (int) (total / 60); + videoPlayerTotalTime[1] = (int) (total % 60); + } + String current, total; + if (videoPlayerCurrentTime[0] >= 60) { + current = String.format(Locale.ROOT, "%02d:%02d:%02d", videoPlayerCurrentTime[0] / 60, videoPlayerCurrentTime[0] % 60, videoPlayerCurrentTime[1]); + } else { + current = String.format(Locale.ROOT, "%02d:%02d", videoPlayerCurrentTime[0], videoPlayerCurrentTime[1]); + } + if (videoPlayerTotalTime[0] >= 60) { + total = String.format(Locale.ROOT, "%02d:%02d:%02d", videoPlayerTotalTime[0] / 60, videoPlayerTotalTime[0] % 60, videoPlayerTotalTime[1]); + } else { + total = String.format(Locale.ROOT, "%02d:%02d", videoPlayerTotalTime[0], videoPlayerTotalTime[1]); + } + videoPlayerTime.setText(String.format(Locale.ROOT, "%s / %s", current, total)); + }; + private void releasePlayer() { if (videoPlayer != null) { playerRetryPlayCount = 0; @@ -595,23 +796,12 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto updateMinMax(scale); } } - - @Override - protected void onDraw(Canvas canvas) { - if (Build.VERSION.SDK_INT >= 21 && isVisible && lastInsets != null) { - WindowInsets insets = (WindowInsets) lastInsets; - if (photoAnimationInProgress != 0) { - blackPaint.setAlpha(photoBackgroundDrawable.getAlpha()); - } else { - blackPaint.setAlpha(255); - } - canvas.drawRect(0, getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight() + insets.getSystemWindowInsetBottom(), blackPaint); - } - } }; windowView.setBackgroundDrawable(photoBackgroundDrawable); windowView.setFocusable(true); windowView.setFocusableInTouchMode(true); + windowView.setClipChildren(false); + windowView.setClipToPadding(false); containerView = new FrameLayoutDrawer(activity) { @Override @@ -621,8 +811,41 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto int y = (ActionBar.getCurrentActionBarHeight() - secretDeleteTimer.getMeasuredHeight()) / 2 + (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0); secretDeleteTimer.layout(secretDeleteTimer.getLeft(), y, secretDeleteTimer.getRight(), y + secretDeleteTimer.getMeasuredHeight()); } + if (secretHint != null && secretDeleteTimer != null) { + int y = (ActionBar.getCurrentActionBarHeight() - secretDeleteTimer.getMeasuredHeight()) / 2 + (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0) + secretDeleteTimer.getMeasuredHeight() - dp(10); + secretHint.layout(secretHint.getLeft(), y, secretHint.getRight(), y + secretHint.getMeasuredHeight()); + } + if (captionScrollView != null) { + int y = ActionBar.getCurrentActionBarHeight() + (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0); + captionScrollView.layout(captionScrollView.getLeft(), y, captionScrollView.getRight(), y + captionScrollView.getMeasuredHeight()); + } + if (navigationBar != null) { + navigationBar.layout(0, bottom - top, right - left, bottom - top + AndroidUtilities.navigationBarHeight); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int width = getMeasuredWidth(); + int height = getMeasuredHeight(); + if (captionScrollView != null) { + captionScrollView.measure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(height - ActionBar.getCurrentActionBarHeight() - (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0) - (seekbarContainer.getVisibility() != View.VISIBLE ? 0 : seekbarContainer.getMeasuredHeight()), MeasureSpec.EXACTLY) + ); + } + if (navigationBar != null) { + navigationBar.measure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(AndroidUtilities.navigationBarHeight, MeasureSpec.EXACTLY) + ); + } } }; + navigationBar = new View(activity); + navigationBar.setBackgroundColor(Theme.ACTION_BAR_PHOTO_VIEWER_COLOR); + containerView.addView(navigationBar, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM)); containerView.setFocusable(false); windowView.addView(containerView); FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) containerView.getLayoutParams(); @@ -644,20 +867,27 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto return insets.consumeSystemWindowInsets(); } }); - containerView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); + containerView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); } gestureDetector = new GestureDetector(containerView.getContext(), this); gestureDetector.setOnDoubleTapListener(this); - actionBar = new ActionBar(activity); + actionBar = new ActionBar(activity) { + @Override + public void setAlpha(float alpha) { + super.setAlpha(alpha); + secretHint.setAlpha(alpha); + secretDeleteTimer.setAlpha(alpha); + } + }; actionBar.setTitleColor(0xffffffff); actionBar.setSubtitleColor(0xffffffff); actionBar.setBackgroundColor(Theme.ACTION_BAR_PHOTO_VIEWER_COLOR); actionBar.setOccupyStatusBar(Build.VERSION.SDK_INT >= 21); actionBar.setItemsBackgroundColor(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR, false); actionBar.setBackButtonImage(R.drawable.ic_ab_back); - actionBar.setTitleRightMargin(AndroidUtilities.dp(70)); + actionBar.setTitleRightMargin(dp(70)); containerView.addView(actionBar, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override @@ -668,9 +898,87 @@ public void onItemClick(int id) { } }); + secretHint = new HintView2(activity, HintView2.DIRECTION_TOP); + secretHint.setJoint(1, -26); + secretHint.setPadding(dp(8), dp(8), dp(8), dp(8)); + containerView.addView(secretHint, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 80, Gravity.TOP | Gravity.RIGHT, 0, 48, 0, 0)); + secretDeleteTimer = new SecretDeleteTimer(activity); containerView.addView(secretDeleteTimer, LayoutHelper.createFrame(119, 48, Gravity.TOP | Gravity.RIGHT, 0, 0, 0, 0)); + final VideoPlayerSeekBar.SeekBarDelegate seekBarDelegate = new VideoPlayerSeekBar.SeekBarDelegate() { + @Override + public void onSeekBarDrag(float progress) { + if (videoPlayer != null) { + long duration = videoPlayer.getDuration(); + if (duration != C.TIME_UNSET) { + videoPlayer.seekTo((long) (progress * duration), false); + } + videoPlayer.play(); + } + } + + @Override + public void onSeekBarContinuousDrag(float progress) { + if (videoPlayer != null) { + videoPlayer.pause(); + long duration = videoPlayer.getDuration(); + if (duration != C.TIME_UNSET) { + videoPlayer.seekTo((long) (progress * duration), false); + } + } + } + }; + seekbarContainer = new VideoPlayerControlFrameLayout(activity); + seekbarBackground = new View(activity); + seekbarBackground.setBackgroundColor(Theme.ACTION_BAR_PHOTO_VIEWER_COLOR); + seekbarContainer.addView(seekbarBackground, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + videoPlayerTime = new SimpleTextView(containerView.getContext()); + videoPlayerTime.setTextColor(0xffffffff); + videoPlayerTime.setGravity(Gravity.RIGHT | Gravity.TOP); + videoPlayerTime.setTextSize(14); + videoPlayerTime.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); + seekbarContainer.addView(videoPlayerTime, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.RIGHT | Gravity.TOP, 0, 15, 12, 0)); + seekbarView = new View(activity) { + @Override + protected void onDraw(Canvas canvas) { + seekbar.draw(canvas, this); + } + }; + seekbar = new VideoPlayerSeekBar(seekbarView); + seekbar.setHorizontalPadding(dp(2)); + seekbar.setColors(0x33ffffff, 0x33ffffff, Color.WHITE, Color.WHITE, Color.WHITE, 0x59ffffff); + seekbar.setDelegate(seekBarDelegate); + seekbarContainer.addView(seekbarView); + containerView.addView(seekbarContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM)); + + textSelectionHelper = new TextSelectionHelper.SimpleTextSelectionHelper(null, new DarkThemeResourceProvider()) { + @Override + public int getParentBottomPadding() { + return 0;//AndroidUtilities.dp(80); + } + }; + textSelectionHelper.allowScrollPrentRelative = true; + textSelectionHelper.useMovingOffset = false; + + captionTextViewSwitcher = new PhotoViewer.CaptionTextViewSwitcher(containerView.getContext()); + captionTextViewSwitcher.setFactory(() -> new PhotoViewer.CaptionTextView(activity, captionScrollView, textSelectionHelper, this::onLinkClick, this::onLinkLongPress)); + captionTextViewSwitcher.setVisibility(View.INVISIBLE); + setCaptionHwLayerEnabled(true); + + playButton = new ImageView(activity); + playButton.setBackground(Theme.createCircleDrawable(dp(64), 0x66000000)); + playButtonDrawable = new PlayPauseDrawable(28); + playButtonDrawable.setCallback(playButton); + playButton.setImageDrawable(playButtonDrawable); + playButton.setScaleType(ImageView.ScaleType.CENTER); + playButton.setScaleX(.6f); + playButton.setScaleY(.6f); + playButton.setAlpha(0f); + playButton.setPivotX(dp(32)); + playButton.setPivotY(dp(32)); + containerView.addView(playButton, LayoutHelper.createFrame(64, 64, Gravity.CENTER)); + windowLayoutParams = new WindowManager.LayoutParams(); windowLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT; windowLayoutParams.format = PixelFormat.TRANSLUCENT; @@ -690,12 +998,410 @@ public void onItemClick(int id) { } centerImage.setParentView(containerView); centerImage.setForceCrossfade(true); + + View overlay = textSelectionHelper.getOverlayView(windowView.getContext()); + if (overlay != null) { + AndroidUtilities.removeFromParent(overlay); + containerView.addView(overlay); + } + textSelectionHelper.setParentView(containerView); + textSelectionHelper.setInvalidateParent(); + } + + private void setCurrentCaption(MessageObject messageObject, final CharSequence _caption, boolean translating, boolean animated) { + final CharSequence caption = AnimatedEmojiSpan.cloneSpans(_caption, AnimatedEmojiDrawable.CACHE_TYPE_ALERT_PREVIEW); + if (captionScrollView == null) { + captionContainer = new FrameLayout(containerView.getContext()); + captionTextViewSwitcher.setContainer(captionContainer); + captionScrollView = new PhotoViewer.CaptionScrollView(containerView.getContext(), captionTextViewSwitcher, captionContainer) { + @Override + protected void onScrollStart() { + AndroidUtilities.cancelRunOnUIThread(hideActionBarRunnable); + } + + @Override + protected void onScrollUpdate() { + if (imageMoveAnimation == null) { + showPlayButton(getScrollY() < getMeasuredHeight() / 3f && isActionBarVisible, true); + } + } + + @Override + protected void onScrollEnd() { + if (isVideo && getScrollY() <= 0) { + AndroidUtilities.runOnUIThread(hideActionBarRunnable, 3000); + } + } + }; + captionTextViewSwitcher.setScrollView(captionScrollView); + captionContainer.setClipChildren(false); + captionScrollView.addView(captionContainer, new ViewGroup.LayoutParams(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + containerView.addView(captionScrollView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.BOTTOM, 0, 0, 0, 0)); + textSelectionHelper.getOverlayView(containerView.getContext()).bringToFront(); + } + if (captionTextViewSwitcher.getParent() != captionContainer) { + captionTextViewSwitcher.setMeasureAllChildren(true); + captionContainer.addView(captionTextViewSwitcher, LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT); + } + + final boolean isCaptionEmpty = TextUtils.isEmpty(caption); + final boolean isCurrentCaptionEmpty = TextUtils.isEmpty(captionTextViewSwitcher.getCurrentView().getText()); + + TextView captionTextView = animated ? captionTextViewSwitcher.getNextView() : captionTextViewSwitcher.getCurrentView(); + +// if (isVideo) { +// if (captionTextView.getMaxLines() != 1) { +// captionTextViewSwitcher.getCurrentView().setMaxLines(1); +// captionTextViewSwitcher.getNextView().setMaxLines(1); +// captionTextViewSwitcher.getCurrentView().setSingleLine(true); +// captionTextViewSwitcher.getNextView().setSingleLine(true); +// captionTextViewSwitcher.getCurrentView().setEllipsize(TextUtils.TruncateAt.END); +// captionTextViewSwitcher.getNextView().setEllipsize(TextUtils.TruncateAt.END); +// } +// } else { + final int maxLines = captionTextView.getMaxLines(); + if (maxLines == 1) { + captionTextViewSwitcher.getCurrentView().setSingleLine(false); + captionTextViewSwitcher.getNextView().setSingleLine(false); + } + final int newCount = Integer.MAX_VALUE; + if (maxLines != newCount) { + captionTextViewSwitcher.getCurrentView().setMaxLines(newCount); + captionTextViewSwitcher.getNextView().setMaxLines(newCount); + captionTextViewSwitcher.getCurrentView().setEllipsize(null); + captionTextViewSwitcher.getNextView().setEllipsize(null); + } +// } + + captionTextView.setScrollX(0); +// dontChangeCaptionPosition = animated && isCaptionEmpty; + boolean withTransition = false; + captionScrollView.dontChangeTopMargin = false; + + if (animated && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + withTransition = true; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + TransitionManager.endTransitions(captionScrollView); + } + final TransitionSet transition = new TransitionSet() + .addTransition(new Fade(Fade.OUT) { + @Override + public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) { + final Animator animator = super.onDisappear(sceneRoot, view, startValues, endValues); + if (!isCurrentCaptionEmpty && isCaptionEmpty && view == captionTextViewSwitcher) { + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + captionScrollView.setVisibility(View.INVISIBLE); + captionScrollView.backgroundAlpha = 1f; + } + }); + ((ObjectAnimator) animator).addUpdateListener(animation -> { + captionScrollView.backgroundAlpha = (float) animation.getAnimatedValue(); + captionScrollView.invalidate(); + }); + } + return animator; + } + }) + .addTransition(new Fade(Fade.IN) { + @Override + public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) { + final Animator animator = super.onAppear(sceneRoot, view, startValues, endValues); + if (isCurrentCaptionEmpty && !isCaptionEmpty && view == captionTextViewSwitcher) { + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + captionScrollView.backgroundAlpha = 1f; + } + }); + ((ObjectAnimator) animator).addUpdateListener(animation -> { + captionScrollView.backgroundAlpha = (float) animation.getAnimatedValue(); + captionScrollView.invalidate(); + }); + } + return animator; + } + }) + .setDuration(200); + + if (!isCurrentCaptionEmpty) { + captionScrollView.dontChangeTopMargin = true; + transition.addTransition(new Transition() { + @Override + public void captureStartValues(TransitionValues transitionValues) { + if (transitionValues.view == captionScrollView) { + transitionValues.values.put("scrollY", captionScrollView.getScrollY()); + } + } + + @Override + public void captureEndValues(TransitionValues transitionValues) { + if (transitionValues.view == captionTextViewSwitcher) { + transitionValues.values.put("translationY", captionScrollView.getPendingMarginTopDiff()); + } + } + + @Override + public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { + if (startValues.view == captionScrollView) { + final ValueAnimator animator = ValueAnimator.ofInt((Integer) startValues.values.get("scrollY"), 0); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + captionTextViewSwitcher.getNextView().setText(null); + captionScrollView.applyPendingTopMargin(); + } + + @Override + public void onAnimationStart(Animator animation) { + captionScrollView.stopScrolling(); + } + }); + animator.addUpdateListener(a -> captionScrollView.scrollTo(0, (Integer) a.getAnimatedValue())); + return animator; + } else if (endValues.view == captionTextViewSwitcher) { + final int endValue = (int) endValues.values.get("translationY"); + if (endValue != 0) { + final ObjectAnimator animator = ObjectAnimator.ofFloat(captionTextViewSwitcher, View.TRANSLATION_Y, 0, endValue); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + captionTextViewSwitcher.setTranslationY(0); + } + }); + return animator; + } + } + return null; + } + }); + } + + if (isCurrentCaptionEmpty && !isCaptionEmpty) { + transition.addTarget(captionTextViewSwitcher); + } + + TransitionManager.beginDelayedTransition(captionScrollView, transition); + } else { + captionTextViewSwitcher.getCurrentView().setText(null); + if (captionScrollView != null) { + captionScrollView.scrollTo(0, 0); + } + } + + boolean switchedToNext = false; + if (!isCaptionEmpty) { + Theme.createChatResources(null, true); + CharSequence str; + if (messageObject != null /*&& captionTranslated*/ && messageObject.messageOwner != null && messageObject.messageOwner.translatedText != null && TextUtils.equals(messageObject.messageOwner.translatedToLanguage, TranslateAlert2.getToLanguage())) { + str = caption; + } else if (messageObject != null && !messageObject.messageOwner.entities.isEmpty()) { + Spannable spannableString = new SpannableString(caption); + messageObject.addEntitiesToText(spannableString, true, false); + if (messageObject.isVideo()) { + MessageObject.addUrlsByPattern(messageObject.isOutOwner(), spannableString, false, 3, (int) messageObject.getDuration(), false); + } + str = Emoji.replaceEmoji(spannableString, captionTextView.getPaint().getFontMetricsInt(), dp(20), false); + } else { + str = Emoji.replaceEmoji(new SpannableStringBuilder(caption), captionTextView.getPaint().getFontMetricsInt(), dp(20), false); + } + captionTextViewSwitcher.setTag(str); + try { + switchedToNext = captionTextViewSwitcher.setText(str, animated, false); + if (captionScrollView != null) { + captionScrollView.updateTopMargin(); + } + } catch (Exception e) { + FileLog.e(e); + } + captionTextView.setScrollY(0); + captionTextView.setTextColor(0xffffffff); + captionTextViewSwitcher.setVisibility(isActionBarVisible ? View.VISIBLE : View.INVISIBLE); + } else { + captionTextViewSwitcher.setText(null, animated); + captionTextViewSwitcher.getCurrentView().setTextColor(0xffffffff); + captionTextViewSwitcher.setVisibility(View.INVISIBLE, !withTransition || isCurrentCaptionEmpty); + captionTextViewSwitcher.setTag(null); + } + if (captionTextViewSwitcher.getCurrentView() instanceof PhotoViewer.CaptionTextView) { + ((PhotoViewer.CaptionTextView) captionTextViewSwitcher.getCurrentView()).setLoading(translating); + } + } + + private void onLinkClick(ClickableSpan link, TextView widget) { +// if (widget != null && link instanceof URLSpan) { +// String url = ((URLSpan) link).getURL(); +// if (url.startsWith("video")) { +// if (videoPlayer != null && currentMessageObject != null) { +// int seconds = Utilities.parseInt(url); +// if (videoPlayer.getDuration() == C.TIME_UNSET) { +// seekToProgressPending = seconds / (float) currentMessageObject.getDuration(); +// } else { +// videoPlayer.seekTo(seconds * 1000L); +// videoPlayerSeekbar.setProgress(seconds * 1000L / (float) videoPlayer.getDuration(), true); +// videoPlayerSeekbarView.invalidate(); +// } +// } +// } else if (url.startsWith("#")) { +// if (parentActivity instanceof LaunchActivity) { +// DialogsActivity fragment = new DialogsActivity(null); +// fragment.setSearchString(url); +// ((LaunchActivity) parentActivity).presentFragment(fragment, false, true); +// closePhoto(false, false); +// } +// } else if (activi != null && (link instanceof URLSpanReplacement || AndroidUtilities.shouldShowUrlInAlert(url))) { +// AlertsCreator.showOpenUrlAlert(parentChatActivity, url, true, true); +// } else { +// link.onClick(widget); +// } +// } else { +// link.onClick(widget); +// } + } + + private void onLinkLongPress(URLSpan link, TextView widget, Runnable onDismiss) { +// int timestamp = -1; +// BottomSheet.Builder builder = new BottomSheet.Builder(parentActivity, false, resourcesProvider, 0xff1C2229); +// if (link.getURL().startsWith("video?")) { +// try { +// String timestampStr = link.getURL().substring(link.getURL().indexOf('?') + 1); +// timestamp = Integer.parseInt(timestampStr); +// } catch (Throwable ignore) { +// +// } +// } +// if (timestamp >= 0) { +// builder.setTitle(AndroidUtilities.formatDuration(timestamp, false)); +// } else { +// builder.setTitle(link.getURL()); +// } +// final int finalTimestamp = timestamp; +// builder.setItems(new CharSequence[]{LocaleController.getString("Open", R.string.Open), LocaleController.getString("Copy", R.string.Copy)}, (dialog, which) -> { +// if (which == 0) { +// onLinkClick(link, widget); +// } else if (which == 1) { +// String url1 = link.getURL(); +// boolean tel = false; +// if (url1.startsWith("mailto:")) { +// url1 = url1.substring(7); +// } else if (url1.startsWith("tel:")) { +// url1 = url1.substring(4); +// tel = true; +// } else if (finalTimestamp >= 0) { +// if (currentMessageObject != null && !currentMessageObject.scheduled) { +// MessageObject messageObject1 = currentMessageObject; +// boolean isMedia = currentMessageObject.isVideo() || currentMessageObject.isRoundVideo() || currentMessageObject.isVoice() || currentMessageObject.isMusic(); +// if (!isMedia && currentMessageObject.replyMessageObject != null) { +// messageObject1 = currentMessageObject.replyMessageObject; +// } +// long dialogId = messageObject1.getDialogId(); +// int messageId = messageObject1.getId(); +// +// if (messageObject1.messageOwner.fwd_from != null) { +// if (messageObject1.messageOwner.fwd_from.saved_from_peer != null) { +// dialogId = MessageObject.getPeerId(messageObject1.messageOwner.fwd_from.saved_from_peer); +// messageId = messageObject1.messageOwner.fwd_from.saved_from_msg_id; +// } else if (messageObject1.messageOwner.fwd_from.from_id != null) { +// dialogId = MessageObject.getPeerId(messageObject1.messageOwner.fwd_from.from_id); +// messageId = messageObject1.messageOwner.fwd_from.channel_post; +// } +// } +// +// if (DialogObject.isChatDialog(dialogId)) { +// TLRPC.Chat currentChat = MessagesController.getInstance(currentAccount).getChat(-dialogId); +// String username = ChatObject.getPublicUsername(currentChat); +// if (username != null) { +// url1 = "https://t.me/" + username + "/" + messageId + "?t=" + finalTimestamp; +// } +// } else { +// TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); +// String username = UserObject.getPublicUsername(user); +// if (user != null && username != null) { +// url1 = "https://t.me/" + username + "/" + messageId + "?t=" + finalTimestamp; +// } +// } +// } +// } +// AndroidUtilities.addToClipboard(url1); +// String bulletinMessage; +// if (tel) { +// bulletinMessage = LocaleController.getString("PhoneCopied", R.string.PhoneCopied); +// } else if (url1.startsWith("#")) { +// bulletinMessage = LocaleController.getString("HashtagCopied", R.string.HashtagCopied); +// } else if (url1.startsWith("@")) { +// bulletinMessage = LocaleController.getString("UsernameCopied", R.string.UsernameCopied); +// } else { +// bulletinMessage = LocaleController.getString("LinkCopied", R.string.LinkCopied); +// } +// if (AndroidUtilities.shouldShowClipboardToast()) { +// BulletinFactory.of(containerView, resourcesProvider).createSimpleBulletin(R.raw.voip_invite, bulletinMessage).show(); +// } +// } +// }); +// builder.setOnPreDismissListener(di -> onDismiss.run()); +// BottomSheet bottomSheet = builder.create(); +// bottomSheet.scrollNavBar = true; +// bottomSheet.show(); +// try { +// containerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); +// } catch (Exception ignore) {} +// bottomSheet.setItemColor(0,0xffffffff, 0xffffffff); +// bottomSheet.setItemColor(1,0xffffffff, 0xffffffff); +// bottomSheet.setBackgroundColor(0xff1C2229); +// bottomSheet.setTitleColor(0xff8A8A8A); +// bottomSheet.setCalcMandatoryInsets(true); +// AndroidUtilities.setNavigationBarColor(bottomSheet.getWindow(), 0xff1C2229, false); +// AndroidUtilities.setLightNavigationBar(bottomSheet.getWindow(), false); +// bottomSheet.scrollNavBar = true; + } + + private boolean playButtonShown; + private void showPlayButton(boolean show, boolean animated) { + show = isVideo && show; + if (playButtonShown == show && animated) { + return; + } + + playButtonShown = show; + playButton.animate().cancel(); + if (animated) { + playButton.animate() + .scaleX(show ? 1f : .6f) + .scaleY(show ? 1f : .6f) + .alpha(show ? 1f : 0f) + .setDuration(340) + .setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT) + .start(); + } else { + playButton.setScaleX(show ? 1f : .6f); + playButton.setScaleY(show ? 1f : .6f); + playButton.setAlpha(show ? 1f : 0f); + } + } + + private void showSecretHint() { + secretHint.setMultilineText(true); + CharSequence text = LocaleController.getString(isVideo ? R.string.VideoShownOnce : R.string.PhotoShownOnce); + secretHint.setMaxWidthPx(HintView2.cutInFancyHalf(text, secretHint.getTextPaint())); + secretHint.setText(text); + secretHint.setInnerPadding(12, 7, 11, 7); + secretHint.setIconMargin(2); + secretHint.setIconTranslate(0, 0); + RLottieDrawable icon = new RLottieDrawable(R.raw.fire_on, "" + R.raw.fire_on, dp(34), dp(34)); + icon.start(); + secretHint.setIcon(icon); + secretHint.show(); + MessagesController.getGlobalMainSettings().edit().putInt("viewoncehint", MessagesController.getGlobalMainSettings().getInt("viewoncehint", 0) + 1).commit(); } private int wasNavigationBarColor; private boolean wasLightNavigationBar; - public void openMedia(MessageObject messageObject, PhotoViewer.PhotoViewerProvider provider, Runnable onOpen) { + private Runnable onClose; + private boolean ignoreDelete; + + public void openMedia(MessageObject messageObject, PhotoViewer.PhotoViewerProvider provider, Runnable onOpen, Runnable onClose) { if (parentActivity == null || messageObject == null || !messageObject.needDrawBluredPreview() || provider == null) { return; } @@ -706,6 +1412,9 @@ public void openMedia(MessageObject messageObject, PhotoViewer.PhotoViewerProvid //messageObject.messageOwner.destroyTime = (int) (System.currentTimeMillis() / 1000 + ConnectionsManager.getInstance().getTimeDifference()) + 4; + ignoreDelete = messageObject.messageOwner.ttl == 0x7FFFFFFF; + this.onClose = onClose; + currentProvider = provider; openTime = System.currentTimeMillis(); closeTime = 0; @@ -740,8 +1449,15 @@ public void openMedia(MessageObject messageObject, PhotoViewer.PhotoViewerProvid closeVideoAfterWatch = false; disableShowCheck = true; centerImage.setManualAlphaAnimator(false); + videoWidth = 0; + videoHeight = 0; - final RectF drawRegion = object.imageReceiver.getDrawRegion(); + final RectF _drawRegion = object.imageReceiver.getDrawRegion(); + RectF drawRegion = new RectF(_drawRegion); + drawRegion.left = Math.max(drawRegion.left, object.imageReceiver.getImageX()); + drawRegion.top = Math.max(drawRegion.top, object.imageReceiver.getImageY()); + drawRegion.right = Math.min(drawRegion.right, object.imageReceiver.getImageX2()); + drawRegion.bottom = Math.min(drawRegion.bottom, object.imageReceiver.getImageY2()); float width = drawRegion.width(); float height = drawRegion.height(); @@ -789,8 +1505,6 @@ public void openMedia(MessageObject messageObject, PhotoViewer.PhotoViewerProvid NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.updateMessageMedia); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.didCreatedNewDeleteTask); currentDialogId = MessageObject.getPeerId(messageObject.messageOwner.peer_id); - toggleActionBar(true, false); - currentMessageObject = messageObject; TLRPC.Document document = messageObject.getDocument(); if (currentThumb != null) { @@ -798,7 +1512,17 @@ public void openMedia(MessageObject messageObject, PhotoViewer.PhotoViewerProvid currentThumb = null; } currentThumb = object.imageReceiver.getThumbBitmapSafe(); + seekbarContainer.setVisibility(View.GONE); if (document != null) { + for (int i = 0; i < document.attributes.size(); ++i) { + TLRPC.DocumentAttribute attr = document.attributes.get(i); + if (attr instanceof TLRPC.TL_documentAttributeVideo) { + TLRPC.TL_documentAttributeVideo attrVideo = (TLRPC.TL_documentAttributeVideo) attr; + videoWidth = attrVideo.w; + videoHeight = attrVideo.h; + break; + } + } if (MessageObject.isGifDocument(document)) { actionBar.setTitle(LocaleController.getString("DisappearingGif", R.string.DisappearingGif)); ImageLocation location; @@ -824,22 +1548,49 @@ public void openMedia(MessageObject messageObject, PhotoViewer.PhotoViewerProvid preparePlayer(file); } isVideo = true; + seekbarContainer.setVisibility(View.VISIBLE); centerImage.setImage(null, null, currentThumb != null ? new BitmapDrawable(currentThumb.bitmap) : null, -1, null, messageObject, 2); long destroyTime = (long) messageObject.messageOwner.destroyTime * 1000; long currentTime = System.currentTimeMillis() + ConnectionsManager.getInstance(currentAccount).getTimeDifference() * 1000L; long timeToDestroy = destroyTime - currentTime; long duration = (long) (messageObject.getDuration() * 1000L); - if (duration > timeToDestroy) { - secretDeleteTimer.setDestroyTime(-1, -1, true); - } else { +// if (duration > timeToDestroy) { +// secretDeleteTimer.setDestroyTime(-1, -1, true); +// } else { secretDeleteTimer.setDestroyTime((long) messageObject.messageOwner.destroyTime * 1000, messageObject.messageOwner.ttl, false); - } +// } } } else { actionBar.setTitle(LocaleController.getString("DisappearingPhoto", R.string.DisappearingPhoto)); TLRPC.PhotoSize sizeFull = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, AndroidUtilities.getPhotoSize()); centerImage.setImage(ImageLocation.getForObject(sizeFull, messageObject.photoThumbsObject), null, currentThumb != null ? new BitmapDrawable(currentThumb.bitmap) : null, -1, null, messageObject, 2); secretDeleteTimer.setDestroyTime((long) messageObject.messageOwner.destroyTime * 1000, messageObject.messageOwner.ttl, false); + + if (sizeFull != null) { + videoWidth = sizeFull.w; + videoHeight = sizeFull.h; + } + } + setCurrentCaption(messageObject, "", false, false); + setCurrentCaption(messageObject, messageObject.caption, false, true); + toggleActionBar(true, false); + showPlayButton(false, false); + playButtonDrawable.setPause(true); + + if (ignoreDelete) { + secretDeleteTimer.setOnce(); + secretDeleteTimer.setOnClickListener(v -> { + if (currentMessageObject == null || currentMessageObject.messageOwner.destroyTime == 0 && currentMessageObject.messageOwner.ttl != 0x7FFFFFFF) { + return; + } + if (secretHint.shown()) { + secretHint.hide(); + return; + } + showSecretHint(); + }); + } else { + secretDeleteTimer.setOnClickListener(null); } try { if (windowView.getParent() != null) { @@ -857,26 +1608,35 @@ public void openMedia(MessageObject messageObject, PhotoViewer.PhotoViewerProvid final Window window = parentActivity.getWindow(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - wasNavigationBarColor = window.getNavigationBarColor(); wasLightNavigationBar = AndroidUtilities.getLightNavigationBar(window); AndroidUtilities.setLightNavigationBar(window, false); - AndroidUtilities.setNavigationBarColor(window, 0xff000000); + AndroidUtilities.setLightNavigationBar(windowView, false); + if (parentActivity instanceof LaunchActivity) { + wasNavigationBarColor = ((LaunchActivity) parentActivity).getNavigationBarColor(); + ((LaunchActivity) parentActivity).animateNavigationBarColor(0xff000000); + } else { + wasNavigationBarColor = window.getNavigationBarColor(); + AndroidUtilities.setNavigationBarColor(window, 0xff000000); + } } imageMoveAnimation = new AnimatorSet(); imageMoveAnimation.playTogether( ObjectAnimator.ofFloat(actionBar, View.ALPHA, 0, 1.0f), - ObjectAnimator.ofFloat(secretDeleteTimer, View.ALPHA, 0, 1.0f), + ObjectAnimator.ofFloat(captionScrollView, View.ALPHA, 0, 1f), + ObjectAnimator.ofFloat(secretHint, View.ALPHA, 0, 1.0f), ObjectAnimator.ofInt(photoBackgroundDrawable, AnimationProperties.COLOR_DRAWABLE_ALPHA, 0, 255), - ObjectAnimator.ofFloat(secretDeleteTimer, View.ALPHA, 0, 1.0f), - ObjectAnimator.ofFloat(this, "animationValue", 0, 1) + ObjectAnimator.ofFloat(this, "animationValue", 0, 1), + ObjectAnimator.ofFloat(seekbarContainer, seekbarContainer.SEEKBAR_ALPHA, 1.0f), + ObjectAnimator.ofFloat(seekbarContainer, View.ALPHA, isVideo ? 1f : 0f) ); photoAnimationInProgress = 3; + final Runnable openRunnable = onOpen; photoAnimationEndRunnable = () -> { photoAnimationInProgress = 0; imageMoveAnimation = null; - if (onOpen != null) { - onOpen.run(); + if (openRunnable != null) { + openRunnable.run(); } if (containerView == null) { return; @@ -887,6 +1647,10 @@ public void openMedia(MessageObject messageObject, PhotoViewer.PhotoViewerProvid containerView.invalidate(); if (closeAfterAnimation) { closePhoto(true, true); + } else { + if (ignoreDelete && MessagesController.getGlobalMainSettings().getInt("viewoncehint", 0) < 3) { + showSecretHint(); + } } }; imageMoveAnimation.setDuration(250); @@ -894,13 +1658,14 @@ public void openMedia(MessageObject messageObject, PhotoViewer.PhotoViewerProvid @Override public void onAnimationEnd(Animator animation) { if (photoAnimationEndRunnable != null) { - photoAnimationEndRunnable.run(); + Runnable r = photoAnimationEndRunnable; photoAnimationEndRunnable = null; + r.run(); } } }); photoTransitionAnimationStartTime = System.currentTimeMillis(); - if (Build.VERSION.SDK_INT >= 18) { + if (Build.VERSION.SDK_INT >= 18 && SharedConfig.getDevicePerformanceClass() == SharedConfig.PERFORMANCE_CLASS_LOW) { containerView.setLayerType(View.LAYER_TYPE_HARDWARE, null); } imageMoveAnimation.setInterpolator(new DecelerateInterpolator()); @@ -916,16 +1681,29 @@ public boolean isShowingImage(MessageObject object) { return isVisible && !disableShowCheck && object != null && currentMessageObject != null && currentMessageObject.getId() == object.getId(); } + private final Runnable hideActionBarRunnable = () -> { + toggleActionBar(false, true); + }; + private void toggleActionBar(boolean show, final boolean animated) { + AndroidUtilities.cancelRunOnUIThread(hideActionBarRunnable); + if (show && isVideo) { + AndroidUtilities.runOnUIThread(hideActionBarRunnable, 3000); + } if (show) { actionBar.setVisibility(View.VISIBLE); } actionBar.setEnabled(show); isActionBarVisible = show; + showPlayButton(show, animated); if (animated) { ArrayList arrayList = new ArrayList<>(); arrayList.add(ObjectAnimator.ofFloat(actionBar, View.ALPHA, show ? 1.0f : 0.0f)); + arrayList.add(ObjectAnimator.ofFloat(seekbarContainer, seekbarContainer.SEEKBAR_ALPHA, show ? 1f : 0f)); + arrayList.add(ObjectAnimator.ofFloat(captionScrollView, View.ALPHA, show ? 1f : 0f)); + arrayList.add(ObjectAnimator.ofFloat(seekbarBackground, View.ALPHA, show ? 1f : 0)); + arrayList.add(ObjectAnimator.ofFloat(navigationBar, View.ALPHA, show ? 1f : 0)); currentActionBarAnimation = new AnimatorSet(); currentActionBarAnimation.playTogether(arrayList); if (!show) { @@ -935,6 +1713,7 @@ public void onAnimationEnd(Animator animation) { if (currentActionBarAnimation != null && currentActionBarAnimation.equals(animation)) { actionBar.setVisibility(View.GONE); currentActionBarAnimation = null; + captionScrollView.scrollTo(0, 0); } } }); @@ -944,8 +1723,12 @@ public void onAnimationEnd(Animator animation) { currentActionBarAnimation.start(); } else { actionBar.setAlpha(show ? 1.0f : 0.0f); + captionScrollView.setAlpha(show ? 1f : 0f); + seekbarBackground.setAlpha(show ? 1f : 0f); + navigationBar.setAlpha(show ? 1f : 0f); if (!show) { actionBar.setVisibility(View.GONE); + captionScrollView.scrollTo(0, 0); } } } @@ -954,6 +1737,10 @@ public boolean isVisible() { return isVisible; } + public void setOnClose(Runnable onClose) { + this.onClose = onClose; + } + public void destroyPhotoViewer() { NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.messagesDeleted); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.updateMessageMedia); @@ -1117,6 +1904,10 @@ private void onDraw(Canvas canvas) { int bitmapWidth = centerImage.getBitmapWidth(); int bitmapHeight = centerImage.getBitmapHeight(); + if (videoWidth != 0 && videoHeight != 0) { + bitmapWidth = videoWidth; + bitmapHeight = videoHeight; + } if (drawTextureView && textureUploaded) { float scale1 = bitmapWidth / (float) bitmapHeight; float scale2 = videoTextureView.getMeasuredWidth() / (float) videoTextureView.getMeasuredHeight(); @@ -1181,8 +1972,9 @@ private boolean checkPhotoAnimation() { if (photoAnimationInProgress != 0) { if (Math.abs(photoTransitionAnimationStartTime - System.currentTimeMillis()) >= 500) { if (photoAnimationEndRunnable != null) { - photoAnimationEndRunnable.run(); + Runnable r = photoAnimationEndRunnable; photoAnimationEndRunnable = null; + r.run(); } photoAnimationInProgress = 0; } @@ -1207,10 +1999,19 @@ public boolean closePhoto(boolean animated, boolean byDelete) { return false; } + if (ignoreDelete && byDelete) { + return false; + } + if (parentActivity != null) { final Window window = parentActivity.getWindow(); AndroidUtilities.setLightNavigationBar(window, wasLightNavigationBar); AndroidUtilities.setNavigationBarColor(window, wasNavigationBarColor); + if (parentActivity instanceof LaunchActivity) { + ((LaunchActivity) parentActivity).animateNavigationBarColor(wasNavigationBarColor); + } else { + AndroidUtilities.setNavigationBarColor(window, wasNavigationBarColor); + } } NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.messagesDeleted); @@ -1240,7 +2041,7 @@ public boolean closePhoto(boolean animated, boolean byDelete) { imageMoveAnimation = new AnimatorSet(); - if (object != null && object.imageReceiver.getThumbBitmap() != null && !byDelete) { + if (object != null && object.imageReceiver.getThumbBitmap() != null && !byDelete && onClose == null) { object.imageReceiver.setVisible(false, true); final RectF drawRegion = object.imageReceiver.getDrawRegion(); @@ -1273,6 +2074,7 @@ public boolean closePhoto(boolean animated, boolean byDelete) { animateToY = translationY >= 0 ? h : -h; } animateToRadius = false; + showPlayButton(false, true); if (isVideo) { videoCrossfadeStarted = false; textureUploaded = false; @@ -1280,7 +2082,11 @@ public boolean closePhoto(boolean animated, boolean byDelete) { ObjectAnimator.ofInt(photoBackgroundDrawable, AnimationProperties.COLOR_DRAWABLE_ALPHA, 0), ObjectAnimator.ofFloat(this, "animationValue", 0, 1), ObjectAnimator.ofFloat(actionBar, View.ALPHA, 0), - ObjectAnimator.ofFloat(secretDeleteTimer, View.ALPHA, 0), + ObjectAnimator.ofFloat(captionScrollView, View.ALPHA, 0), + ObjectAnimator.ofFloat(navigationBar, View.ALPHA, 0), + ObjectAnimator.ofFloat(seekbarContainer, seekbarContainer.SEEKBAR_ALPHA, 0f), + ObjectAnimator.ofFloat(seekbarContainer, View.ALPHA, 0f), + ObjectAnimator.ofFloat(secretHint, View.ALPHA, 0), ObjectAnimator.ofFloat(this, "videoCrossfadeAlpha", 0) ); } else { @@ -1289,7 +2095,11 @@ public boolean closePhoto(boolean animated, boolean byDelete) { ObjectAnimator.ofInt(photoBackgroundDrawable, AnimationProperties.COLOR_DRAWABLE_ALPHA, 0), ObjectAnimator.ofFloat(this, "animationValue", 0, 1), ObjectAnimator.ofFloat(actionBar, View.ALPHA, 0), - ObjectAnimator.ofFloat(secretDeleteTimer, View.ALPHA, 0), + ObjectAnimator.ofFloat(captionScrollView, View.ALPHA, 0), + ObjectAnimator.ofFloat(navigationBar, View.ALPHA, 0), + ObjectAnimator.ofFloat(seekbarContainer, seekbarContainer.SEEKBAR_ALPHA, 0f), + ObjectAnimator.ofFloat(seekbarContainer, View.ALPHA, 0f), + ObjectAnimator.ofFloat(secretHint, View.ALPHA, 0), ObjectAnimator.ofFloat(centerImage, "currentAlpha", 0.0f) ); } @@ -1315,8 +2125,9 @@ public void onAnimationEnd(Animator animation) { isVisible = false; AndroidUtilities.runOnUIThread(() -> { if (photoAnimationEndRunnable != null) { - photoAnimationEndRunnable.run(); + Runnable r = photoAnimationEndRunnable; photoAnimationEndRunnable = null; + r.run(); } }); } @@ -1327,12 +2138,17 @@ public void onAnimationEnd(Animator animation) { } imageMoveAnimation.start(); } else { + showPlayButton(false, true); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether( ObjectAnimator.ofFloat(containerView, View.SCALE_X, 0.9f), ObjectAnimator.ofFloat(containerView, View.SCALE_Y, 0.9f), ObjectAnimator.ofInt(photoBackgroundDrawable, AnimationProperties.COLOR_DRAWABLE_ALPHA, 0), - ObjectAnimator.ofFloat(actionBar, View.ALPHA, 0) + ObjectAnimator.ofFloat(actionBar, View.ALPHA, 0), + ObjectAnimator.ofFloat(captionScrollView, View.ALPHA, 0), + ObjectAnimator.ofFloat(navigationBar, View.ALPHA, 0), + ObjectAnimator.ofFloat(seekbarContainer, seekbarContainer.SEEKBAR_ALPHA, 0f), + ObjectAnimator.ofFloat(seekbarContainer, View.ALPHA, 0f) ); photoAnimationInProgress = 2; photoAnimationEndRunnable = () -> { @@ -1353,8 +2169,9 @@ public void onAnimationEnd(Animator animation) { @Override public void onAnimationEnd(Animator animation) { if (photoAnimationEndRunnable != null) { - photoAnimationEndRunnable.run(); + Runnable r = photoAnimationEndRunnable; photoAnimationEndRunnable = null; + r.run(); } } }); @@ -1364,6 +2181,10 @@ public void onAnimationEnd(Animator animation) { } animatorSet.start(); } + if (onClose != null) { + onClose.run(); + onClose = null; + } return true; } @@ -1471,10 +2292,10 @@ private boolean processTouchEvent(MotionEvent ev) { } float dx = Math.abs(ev.getX() - moveStartX); float dy = Math.abs(ev.getY() - dragY); - if (dx > AndroidUtilities.dp(3) || dy > AndroidUtilities.dp(3)) { + if (dx > dp(3) || dy > dp(3)) { discardTap = true; } - if (canDragDown && !draggingDown && scale == 1 && dy >= AndroidUtilities.dp(30) && dy / 2 > dx) { + if (canDragDown && !draggingDown && scale == 1 && dy >= dp(30) && dy / 2 > dx) { draggingDown = true; moving = false; dragY = ev.getY(); @@ -1488,7 +2309,7 @@ private boolean processTouchEvent(MotionEvent ev) { } else if (!invalidCoords && animationStartTime == 0) { float moveDx = moveStartX - ev.getX(); float moveDy = moveStartY - ev.getY(); - if (moving || scale == 1 && Math.abs(moveDy) + AndroidUtilities.dp(12) < Math.abs(moveDx) || scale != 1) { + if (moving || scale == 1 && Math.abs(moveDy) + dp(12) < Math.abs(moveDx) || scale != 1) { if (!moving) { moveDx = 0; moveDy = 0; @@ -1682,7 +2503,21 @@ public boolean onSingleTapConfirmed(MotionEvent e) { if (discardTap) { return false; } - toggleActionBar(!isActionBarVisible, true); + if (videoPlayer != null && isActionBarVisible && ( + e.getX() >= playButton.getX() && + e.getY() >= playButton.getY() && + e.getX() <= playButton.getX() + playButton.getMeasuredWidth() && + e.getX() <= playButton.getX() + playButton.getMeasuredWidth() + )) { + videoPlayer.setPlayWhenReady(!videoPlayer.getPlayWhenReady()); + if (videoPlayer.getPlayWhenReady()) { + toggleActionBar(true, true); + } else { + showPlayButton(true, true); + } + } else { + toggleActionBar(!isActionBarVisible, true); + } return true; } @@ -1724,4 +2559,200 @@ public boolean onDoubleTapEvent(MotionEvent e) { private boolean scaleToFill() { return false; } + + + private class VideoPlayerControlFrameLayout extends FrameLayout { + + private float progress = 1f; + private boolean seekBarTransitionEnabled = true; + private boolean translationYAnimationEnabled = true; + private boolean ignoreLayout; + private int parentWidth; + private int parentHeight; + + private int lastTimeWidth; + private FloatValueHolder timeValue = new FloatValueHolder(0); + private SpringAnimation timeSpring = new SpringAnimation(timeValue) + .setSpring(new SpringForce(0) + .setStiffness(750f) + .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)) + .addUpdateListener((animation, value, velocity) -> { + int extraWidth; + if (parentWidth > parentHeight) { + extraWidth = dp(48); + } else { + extraWidth = 0; + } + + seekbar.setSize((int) (getMeasuredWidth() - dp(2 + 14) - value - extraWidth), getMeasuredHeight()); + }); + + public VideoPlayerControlFrameLayout(@NonNull Context context) { + super(context); + setWillNotDraw(false); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (progress < 1f) { + return false; + } + if (seekbar.onTouch(event.getAction(), event.getX() - dp(2), event.getY())) { + getParent().requestDisallowInterceptTouchEvent(true); + seekbarView.invalidate(); + return true; + } + return true; + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + timeValue.setValue(0); + lastTimeWidth = 0; + } + + @Override + public void requestLayout() { + if (ignoreLayout) { + return; + } + super.requestLayout(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int extraWidth; + ignoreLayout = true; + LayoutParams layoutParams = (LayoutParams) videoPlayerTime.getLayoutParams(); + if (parentWidth > parentHeight) { + extraWidth = dp(48); + layoutParams.rightMargin = dp(47); + } else { + extraWidth = 0; + layoutParams.rightMargin = dp(12); + } + ignoreLayout = false; + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + long duration; + if (videoPlayer != null) { + duration = videoPlayer.getDuration(); + if (duration == C.TIME_UNSET) { + duration = 0; + } + } else { + duration = 0; + } + duration /= 1000; + + String durationStr; + if (duration / 60 > 60) { + durationStr = String.format(Locale.ROOT, "%02d:%02d:%02d", (duration / 60) / 60, (duration / 60) % 60, duration % 60); + } else { + durationStr = String.format(Locale.ROOT, "%02d:%02d", duration / 60, duration % 60); + } + + int size = (int) Math.ceil(videoPlayerTime.getPaint().measureText(String.format(Locale.ROOT, "%1$s / %1$s", durationStr))); + timeSpring.cancel(); + if (lastTimeWidth != 0 && timeValue.getValue() != size) { + timeSpring.getSpring().setFinalPosition(size); + timeSpring.start(); + } else { + seekbar.setSize(getMeasuredWidth() - dp(2 + 14) - size - extraWidth, getMeasuredHeight()); + timeValue.setValue(size); + } + lastTimeWidth = size; + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + float progress = 0; + if (videoPlayer != null) { + progress = videoPlayer.getCurrentPosition() / (float) videoPlayer.getDuration(); + } + seekbar.setProgress(progress); + } + + public float getProgress() { + return progress; + } + + public void setProgress(float progress) { + if (this.progress != progress) { + this.progress = progress; + onProgressChanged(progress); + } + } + + public final Property SEEKBAR_ALPHA = new AnimationProperties.FloatProperty("progress") { + @Override + public void setValue(VideoPlayerControlFrameLayout object, float value) { + object.setProgress(value); + } + + @Override + public Float get(VideoPlayerControlFrameLayout object) { + return object.getProgress(); + } + }; + + private void onProgressChanged(float progress) { + videoPlayerTime.setAlpha(progress); + if (seekBarTransitionEnabled) { + videoPlayerTime.setPivotX(videoPlayerTime.getWidth()); + videoPlayerTime.setPivotY(videoPlayerTime.getHeight()); + videoPlayerTime.setScaleX(1f - 0.1f * (1f - progress)); + videoPlayerTime.setScaleY(1f - 0.1f * (1f - progress)); + seekbar.setTransitionProgress(1f - progress); + } else { + if (translationYAnimationEnabled) { + setTranslationY(AndroidUtilities.dpf2(24) * (1f - progress)); + } + seekbarView.setAlpha(progress); + } + } + + public boolean isSeekBarTransitionEnabled() { + return seekBarTransitionEnabled; + } + + public void setSeekBarTransitionEnabled(boolean seekBarTransitionEnabled) { + if (this.seekBarTransitionEnabled != seekBarTransitionEnabled) { + this.seekBarTransitionEnabled = seekBarTransitionEnabled; + if (seekBarTransitionEnabled) { + setTranslationY(0); + seekbarView.setAlpha(1f); + } else { + videoPlayerTime.setScaleX(1f); + videoPlayerTime.setScaleY(1f); + seekbar.setTransitionProgress(0f); + } + onProgressChanged(progress); + } + } + + public void setTranslationYAnimationEnabled(boolean translationYAnimationEnabled) { + if (this.translationYAnimationEnabled != translationYAnimationEnabled) { + this.translationYAnimationEnabled = translationYAnimationEnabled; + if (!translationYAnimationEnabled) { + setTranslationY(0); + } + onProgressChanged(progress); + } + } + } + + + private boolean captionHwLayerEnabled; + + private void setCaptionHwLayerEnabled(boolean enabled) { + if (captionHwLayerEnabled != enabled) { + captionHwLayerEnabled = enabled; + captionTextViewSwitcher.setLayerType(View.LAYER_TYPE_HARDWARE, null); + captionTextViewSwitcher.getCurrentView().setLayerType(View.LAYER_TYPE_HARDWARE, null); + captionTextViewSwitcher.getNextView().setLayerType(View.LAYER_TYPE_HARDWARE, null); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/DarkThemeResourceProvider.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/DarkThemeResourceProvider.java index 8feb636adf..d459957890 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/DarkThemeResourceProvider.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/DarkThemeResourceProvider.java @@ -49,6 +49,7 @@ public DarkThemeResourceProvider() { sparseIntArray.put(Theme.key_sheet_scrollUp, ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.2f)); sparseIntArray.put(Theme.key_dialogTextBlack, -592138); + sparseIntArray.put(Theme.key_dialogTextGray3, -8553091); sparseIntArray.put(Theme.key_windowBackgroundWhiteBlueIcon, Color.WHITE); sparseIntArray.put(Theme.key_chat_emojiPanelStickerSetName, 0x73ffffff); sparseIntArray.put(Theme.key_chat_emojiPanelStickerSetNameIcon, 0x73ffffff); @@ -96,19 +97,20 @@ public DarkThemeResourceProvider() { 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_dialogFloatingButton, -15033089); 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_windowBackgroundGray, Color.BLACK); - sparseIntArray.put(Theme.key_windowBackgroundWhiteBlueHeader, -9652488); + sparseIntArray.put(Theme.key_windowBackgroundWhiteBlueHeader, 0xFF1A9CFF); 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_windowBackgroundWhiteGrayText4, 0xFF808080); sparseIntArray.put(Theme.key_switchTrack, 0xFF636363); sparseIntArray.put(Theme.key_switchTrackChecked, 0xFF1A9CFF); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/DialogStoriesCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/DialogStoriesCell.java index 1e6117745d..08aa749bd2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/DialogStoriesCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/DialogStoriesCell.java @@ -877,7 +877,7 @@ public void onUserLongPressed(View view, long dialogId) { public void openStoryRecorder() { final StoriesController.StoryLimit storyLimit = MessagesController.getInstance(currentAccount).getStoriesController().checkStoryLimit(); if (storyLimit != null) { - fragment.showDialog(new LimitReachedBottomSheet(fragment, getContext(), storyLimit.getLimitReachedType(), currentAccount)); + fragment.showDialog(new LimitReachedBottomSheet(fragment, getContext(), storyLimit.getLimitReachedType(), currentAccount, fragment.getResourceProvider())); return; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/MessageMediaStoryFull.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/MessageMediaStoryFull.java index 7d508e0e41..3c5952a2bc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/MessageMediaStoryFull.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/MessageMediaStoryFull.java @@ -1,5 +1,8 @@ package org.telegram.ui.Stories; +import org.telegram.messenger.DialogObject; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.UserConfig; import org.telegram.tgnet.AbstractSerializedData; import org.telegram.tgnet.TLRPC; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/PeerStoriesView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/PeerStoriesView.java index ae4eaf1881..9c00ddbde8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/PeerStoriesView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/PeerStoriesView.java @@ -1813,7 +1813,7 @@ public void onTextChanged(CharSequence text, boolean bigChange) { } if (mentionContainer.getAdapter() != null) { mentionContainer.setDialogId(dialogId); - mentionContainer.getAdapter().setUserOrChar(MessagesController.getInstance(currentAccount).getUser(dialogId), null); + mentionContainer.getAdapter().setUserOrChat(MessagesController.getInstance(currentAccount).getUser(dialogId), null); mentionContainer.getAdapter().searchUsernameOrHashtag(text, chatActivityEnterView.getCursorPosition(), null, false, false); } invalidate(); @@ -2723,7 +2723,7 @@ protected void dispatchDraw(Canvas canvas) { //emojiReactionEffect.setBounds(0, 0, size, size); emojiReactionEffect.setBounds((int) (cX - size / 2f), (int) (cY - size / 2f), (int) (cX + size / 2f), (int) (cY + size / 2f)); emojiReactionEffect.draw(canvas); - if (emojiReactionEffect.done()) { + if (emojiReactionEffect.isDone()) { emojiReactionEffect.removeView(this); emojiReactionEffect = null; drawReactionEffect = false; @@ -2841,23 +2841,18 @@ public Activity getParentActivity() { return activity; } - @Override - public Theme.ResourcesProvider getResourceProvider() { - return new WrappedResourceProvider(resourcesProvider) { - @Override - public void appendColors() { - sparseIntArray.append(Theme.key_dialogBackground, 0xFF1F1F1F); - sparseIntArray.append(Theme.key_windowBackgroundGray, 0xFF333333); - } - }; - } - @Override public boolean presentFragment(BaseFragment fragment) { storyViewer.presentFragment(fragment); return true; } - }, activity, storyLimit.getLimitReachedType(), currentAccount); + }, activity, storyLimit.getLimitReachedType(), currentAccount, new WrappedResourceProvider(resourcesProvider) { + @Override + public void appendColors() { + sparseIntArray.append(Theme.key_dialogBackground, 0xFF1F1F1F); + sparseIntArray.append(Theme.key_windowBackgroundGray, 0xFF333333); + } + }); delegate.showDialog(sheet); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/ProfileStoriesView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/ProfileStoriesView.java index 43b959a42e..0386bb67c2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/ProfileStoriesView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/ProfileStoriesView.java @@ -16,9 +16,7 @@ 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; @@ -37,7 +35,6 @@ 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; @@ -428,10 +425,11 @@ public void onAnimationEnd(Animator animation) { @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(); + float insetMain = 0; + float ax = avatarContainer.getX() + insetMain * avatarContainer.getScaleX(); + float ay = avatarContainer.getY() + insetMain * avatarContainer.getScaleY(); + float aw = (avatarContainer.getWidth() - insetMain * 2) * avatarContainer.getScaleX(); + float ah = (avatarContainer.getHeight() - insetMain * 2) * avatarContainer.getScaleY(); rect1.set(ax, ay, ax + aw, ay + ah); float maxX = this.left; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoriesPreviewView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoriesPreviewView.java index e8e3e2543f..c4cd572698 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoriesPreviewView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoriesPreviewView.java @@ -25,6 +25,7 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; @@ -264,7 +265,6 @@ private void scrollToClosest() { 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); } @@ -407,6 +407,7 @@ public class ImageHolder { int position; StaticLayout layout; TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + SelfStoryViewsView.StoryItemInternal storyItem; public ImageHolder() { receiver.setAllowLoadingOnAttachedOnly(true); @@ -416,7 +417,7 @@ public ImageHolder() { } void onBind(int position) { - SelfStoryViewsView.StoryItemInternal storyItem = storyItems.get(position); + storyItem = storyItems.get(position); if (isAttachedToWindow) { receiver.onAttachedToWindow(); } @@ -425,6 +426,10 @@ void onBind(int position) { } else { StoriesUtilities.setImage(receiver, storyItem.uploadingStory); } + updateLayout(); + } + + private void updateLayout() { SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(); if (storyItem.storyItem != null) { formatCounterText(spannableStringBuilder, storyItem.storyItem.views, false); @@ -463,6 +468,10 @@ public void draw(Canvas canvas, float alpha, float scale, int x, int y, int widt canvas.restore(); } } + + public void update() { + updateLayout(); + } } private void formatCounterText(SpannableStringBuilder spannableStringBuilder, TLRPC.StoryViews storyViews, boolean twoLines) { @@ -499,5 +508,11 @@ protected void onDetachedFromWindow() { } lastDrawnImageReceivers.clear(); } + + public void update() { + for (int i = 0; i < lastDrawnImageReceivers.size(); i++) { + lastDrawnImageReceivers.get(i).update(); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoryViewsPage.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoryViewsPage.java index 567625ec58..994d48a14c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoryViewsPage.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoryViewsPage.java @@ -3,7 +3,6 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; -import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; @@ -12,9 +11,7 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; -import android.text.Layout; import android.text.SpannableStringBuilder; -import android.text.TextPaint; import android.text.TextUtils; import android.util.TypedValue; import android.view.Gravity; @@ -29,11 +26,9 @@ import androidx.annotation.NonNull; import androidx.core.graphics.ColorUtils; -import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.google.android.exoplayer2.util.Consumer; -import com.google.android.exoplayer2.util.Log; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.BuildVars; @@ -52,7 +47,6 @@ import org.telegram.ui.ActionBar.ActionBarPopupWindow; import org.telegram.ui.ActionBar.AdjustPanLayoutHelper; import org.telegram.ui.ActionBar.BaseFragment; -import org.telegram.ui.ActionBar.SimpleTextView; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Cells.FixedHeightEmptyCell; import org.telegram.ui.Cells.ReactedUserHolderView; @@ -62,7 +56,6 @@ import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.CustomPopupMenu; import org.telegram.ui.Components.EmojiPacksAlert; -import org.telegram.ui.Components.FillLastGridLayoutManager; import org.telegram.ui.Components.FillLastLinearLayoutManager; import org.telegram.ui.Components.FlickerLoadingView; import org.telegram.ui.Components.ItemOptions; @@ -81,7 +74,6 @@ import org.telegram.ui.PremiumPreviewFragment; import org.telegram.ui.ProfileActivity; import org.telegram.ui.RecyclerListViewScroller; -import org.telegram.ui.Stories.recorder.HintView2; import org.telegram.ui.Stories.recorder.StoryPrivacyBottomSheet; import java.util.ArrayList; @@ -141,6 +133,7 @@ public class SelfStoryViewsPage extends FrameLayout implements NotificationCente Drawable shadowDrawable; private boolean checkAutoscroll; private boolean showServerErrorText; + private long dialogId; private boolean isStoryShownToUser(TLRPC.TL_storyView view) { if (MessagesController.getInstance(currentAccount).getStoriesController().isBlocked(view)) { @@ -706,6 +699,10 @@ public void onKeyboardShown() { } public boolean onBackPressed() { + if (popupMenu != null && popupMenu.isShowing()) { + popupMenu.dismiss(); + return true; + } if (Math.abs(topViewsContainer.getTranslationY() - recyclerListView.getPaddingTop()) > AndroidUtilities.dp(2)) { recyclerListView.dispatchTouchEvent(AndroidUtilities.emptyMotionEvent()); recyclerListView.smoothScrollToPosition(0); @@ -1069,12 +1066,21 @@ public void loadNext() { if (storyItem.views == null) { storyItem.views = new TLRPC.TL_storyViews(); } + boolean counterUpdated = false; 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; + counterUpdated = true; + } + if (storyItem.views.reactions_count != res.reactions_count) { + storyItem.views.reactions_count = res.reactions_count; + counterUpdated = true; + } + if (counterUpdated) { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.storiesUpdated); } } else { hasNext = false; @@ -1224,6 +1230,7 @@ public HeaderView(@NonNull Context context) { ImageView imageView = new ImageView(getContext()); imageView.setScaleType(ImageView.ScaleType.FIT_XY); imageView.setImageDrawable(replacableDrawable); + imageView.setPadding(AndroidUtilities.dp(1), AndroidUtilities.dp(1), AndroidUtilities.dp(1), AndroidUtilities.dp(1)); buttonContainer.addView(imageView, LayoutHelper.createLinear(26, 26)); ImageView arrowImage = new ImageView(getContext()); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesController.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesController.java index 7a366c532a..19ac920622 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesController.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesController.java @@ -1938,7 +1938,7 @@ public void updateDeletedStoriesInLists(long userId, List story public void destroyStoryList(StoriesList list) { if (storiesLists[list.type] != null) { - storiesLists[list.type].remove(list.userId); + storiesLists[list.type].remove(list.dialogId); } } @@ -1966,7 +1966,7 @@ public void unlink(int id) { public static final int TYPE_ARCHIVE = 1; public final int currentAccount; - public final long userId; + public final long dialogId; public final int type; public final HashMap> groupedByDay = new HashMap<>(); @@ -2055,7 +2055,7 @@ private boolean filter(MessageObject msg, boolean photos, boolean videos) { private StoriesList(int currentAccount, long userId, int type, Utilities.Callback destroy) { this.currentAccount = currentAccount; - this.userId = userId; + this.dialogId = userId; this.type = type; this.destroyRunnable = () -> destroy.run(this); @@ -2076,16 +2076,12 @@ private void preloadCache() { final ArrayList loadedUsers = 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"); - } + cursor = database.queryFinalized(String.format(Locale.US, "SELECT data FROM profile_stories WHERE dialog_id = %d AND type = %d ORDER BY story_id DESC", dialogId, type)); 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.dialogId = dialogId; storyItem.messageId = storyItem.id; MessageObject msg = new MessageObject(currentAccount, storyItem); for (TLRPC.PrivacyRule rule : storyItem.privacy) { @@ -2115,7 +2111,7 @@ private void preloadCache() { } AndroidUtilities.runOnUIThread(() -> { - FileLog.d("StoriesList "+type+"{"+userId+"} preloadCache {" + storyItemMessageIds(cacheResult) + "}"); + FileLog.d("StoriesList "+type+"{"+ dialogId +"} preloadCache {" + storyItemMessageIds(cacheResult) + "}"); preloading = false; MessagesController.getInstance(currentAccount).putUsers(loadedUsers, true); if (invalidateAfterPreload) { @@ -2218,11 +2214,7 @@ public void invalidateCache() { 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(); - } + database.executeFast(String.format(Locale.US, "DELETE FROM profile_stories WHERE dialog_id = %d AND type = %d", dialogId, type)).stepThis().dispose(); } catch (Throwable e) { storage.checkSQLException(e); } @@ -2246,16 +2238,11 @@ private void saveCache() { SQLitePreparedStatement state = null; ArrayList toSave = new ArrayList<>(); fill(toSave, true, true); - FileLog.d("StoriesList "+type+"{"+userId+"} saveCache {" + storyItemMessageIds(toSave) + "}"); + FileLog.d("StoriesList " + type + "{"+ dialogId +"} saveCache {" + storyItemMessageIds(toSave) + "}"); 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(?, ?)"); - } + database.executeFast(String.format(Locale.US, "DELETE FROM profile_stories WHERE dialog_id = %d AND type = %d", dialogId, type)).stepThis().dispose(); + state = database.executeFast("REPLACE INTO profile_stories VALUES(?, ?, ?, ?)"); for (int i = 0; i < toSave.size(); ++i) { MessageObject messageObject = toSave.get(i); @@ -2268,14 +2255,10 @@ private void saveCache() { 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.bindLong(1, dialogId); + state.bindInteger(2, storyItem.id); + state.bindByteBuffer(3, data); + state.bindInteger(4, type); state.step(); data.reuse(); } @@ -2298,7 +2281,7 @@ private boolean canLoad() { if (lastLoadTime == null) { return true; } - final int key = Objects.hash(currentAccount, type, userId); + final int key = Objects.hash(currentAccount, type, dialogId); Long time = lastLoadTime.get(key); if (time == null) { return true; @@ -2308,7 +2291,7 @@ private boolean canLoad() { private void resetCanLoad() { if (lastLoadTime != null) { - lastLoadTime.remove(Objects.hash(currentAccount, type, userId)); + lastLoadTime.remove(Objects.hash(currentAccount, type, dialogId)); } } @@ -2325,7 +2308,7 @@ public boolean load(boolean force, final int count) { TLObject request; if (type == TYPE_PINNED) { TLRPC.TL_stories_getPinnedStories req = new TLRPC.TL_stories_getPinnedStories(); - req.user_id = MessagesController.getInstance(currentAccount).getInputUser(userId); + req.user_id = MessagesController.getInstance(currentAccount).getInputUser(dialogId); if (!loadedObjects.isEmpty()) { req.offset_id = offset_id = loadedObjects.last(); } else { @@ -2343,7 +2326,7 @@ public boolean load(boolean force, final int count) { req.limit = count; request = req; } - FileLog.d("StoriesList " + type + "{"+userId+"} load"); + FileLog.d("StoriesList " + type + "{"+dialogId+"} load"); loading = true; ConnectionsManager.getInstance(currentAccount).sendRequest(request, (response, err) -> { @@ -2355,7 +2338,7 @@ public boolean load(boolean force, final int count) { newMessageObjects.add(toMessageObject(storyItem)); } AndroidUtilities.runOnUIThread(() -> { - FileLog.d("StoriesList " + type + "{"+userId+"} loaded {" + storyItemMessageIds(newMessageObjects) + "}"); + FileLog.d("StoriesList " + type + "{"+dialogId+"} loaded {" + storyItemMessageIds(newMessageObjects) + "}"); MessagesController.getInstance(currentAccount).putUsers(stories.users, false); loading = false; @@ -2392,7 +2375,7 @@ public boolean load(boolean force, final int count) { if (lastLoadTime == null) { lastLoadTime = new HashMap<>(); } - lastLoadTime.put(Objects.hash(currentAccount, type, userId), System.currentTimeMillis()); + lastLoadTime.put(Objects.hash(currentAccount, type, dialogId), System.currentTimeMillis()); } else { resetCanLoad(); } @@ -2424,7 +2407,7 @@ public boolean load(boolean force, final int count) { // } public void updateDeletedStories(List storyItems) { - FileLog.d("StoriesList " + type + "{"+userId+"} updateDeletedStories {" + storyItemIds(storyItems) + "}"); + FileLog.d("StoriesList " + type + "{"+dialogId+"} updateDeletedStories {" + storyItemIds(storyItems) + "}"); if (storyItems == null) { return; } @@ -2452,7 +2435,7 @@ public void updateDeletedStories(List storyItems) { } public void updateStories(List storyItems) { - FileLog.d("StoriesList " + type + "{"+userId+"} updateStories {" + storyItemIds(storyItems) + "}"); + FileLog.d("StoriesList " + type + "{"+dialogId+"} updateStories {" + storyItemIds(storyItems) + "}"); if (storyItems == null) { return; } @@ -2517,7 +2500,7 @@ public boolean equal(TLRPC.StoryItem a, TLRPC.StoryItem b) { } private MessageObject toMessageObject(TLRPC.StoryItem storyItem) { - storyItem.dialogId = userId; + storyItem.dialogId = dialogId; storyItem.messageId = storyItem.id; MessageObject msg = new MessageObject(currentAccount, storyItem); msg.generateThumbs(false); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesStorage.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesStorage.java index ff3012f0a5..110a54783f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesStorage.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesStorage.java @@ -75,18 +75,14 @@ public void getAllStories(Consumer consumer) { 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, custom_params FROM stories WHERE dialog_id = %d", dialogId)); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, custom_params 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); - NativeByteBuffer customData = cursor.byteBufferValue(3); + NativeByteBuffer customData = cursor.byteBufferValue(1); if (data != null) { TLRPC.StoryItem storyItem = TLRPC.StoryItem.TLdeserialize(data, data.readInt32(true), true); storyItem.dialogId = dialogId; - storyItem.attachPath = path; - storyItem.firstFramePath = firstFramePath; StoryCustomParamsHelper.readLocalParams(storyItem, customData); storyItems.add(storyItem); data.reuse(); @@ -171,18 +167,10 @@ public void putStoriesInternal(long dialogId, TLRPC.TL_userStories userStories) try { if (userStories != null) { ArrayList storyItems = userStories.stories; - SQLitePreparedStatement state = database.executeFast("REPLACE INTO stories VALUES(?, ?, ?, ?, ?, ?)"); + 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; @@ -193,21 +181,11 @@ public void putStoriesInternal(long dialogId, TLRPC.TL_userStories userStories) 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); - } NativeByteBuffer nativeByteBuffer = StoryCustomParamsHelper.writeLocalParams(storyItem); if (nativeByteBuffer != null) { - state.bindByteBuffer(6, nativeByteBuffer); + state.bindByteBuffer(4, nativeByteBuffer); } else { - state.bindNull(6); + state.bindNull(4); } if (nativeByteBuffer != null) { nativeByteBuffer.reuse(); @@ -226,15 +204,7 @@ public void putStoriesInternal(long dialogId, TLRPC.TL_userStories userStories) 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(); - } + SQLitePreparedStatement state = database.executeFast("REPLACE INTO stories VALUES(?, ?, ?, ?)"); if (storyItem instanceof TLRPC.TL_storyItemDeleted) { FileLog.e("putStoryInternal: try write deleted story"); return; @@ -245,21 +215,11 @@ public void putStoryInternal(long dialogId, TLRPC.StoryItem storyItem) { 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); - } NativeByteBuffer nativeByteBuffer = StoryCustomParamsHelper.writeLocalParams(storyItem); if (nativeByteBuffer != null) { - state.bindByteBuffer(6, nativeByteBuffer); + state.bindByteBuffer(4, nativeByteBuffer); } else { - state.bindNull(6); + state.bindNull(4); } if (nativeByteBuffer != null) { nativeByteBuffer.reuse(); @@ -336,18 +296,14 @@ private TLRPC.StoryItem getStoryInternal(long user_id, int storyId) { SQLiteCursor cursor = null; TLRPC.StoryItem storyItem = null; try { - cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, local_path, local_thumb_path, custom_params FROM stories WHERE dialog_id = %d AND story_id = %d", user_id, storyId)); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, custom_params 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); - NativeByteBuffer customData = cursor.byteBufferValue(3); + NativeByteBuffer customData = cursor.byteBufferValue(1); if (data != null) { storyItem = TLRPC.StoryItem.TLdeserialize(data, data.readInt32(true), true); storyItem.dialogId = user_id; - storyItem.attachPath = path; - storyItem.firstFramePath = thumbPath; data.reuse(); } if (storyItem != null) { @@ -387,17 +343,13 @@ private TLRPC.TL_userStories getStoriesInternal(long dialogId) { cursor.dispose(); cursor = null; - cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, local_path, local_thumb_path, custom_params FROM stories WHERE dialog_id = %d", dialogId)); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, custom_params 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); - NativeByteBuffer customData = cursor.byteBufferValue(3); + NativeByteBuffer customData = cursor.byteBufferValue(1); if (data != null) { TLRPC.StoryItem storyItem = TLRPC.StoryItem.TLdeserialize(data, data.readInt32(true), true); - storyItem.attachPath = path; - storyItem.firstFramePath = thumbPath; StoryCustomParamsHelper.readLocalParams(storyItem, customData); storyItems.add(storyItem); data.reuse(); @@ -445,17 +397,7 @@ private void updateStoryItemInternal(long dialogId, TLRPC.StoryItem storyItem) { 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 = database.executeFast("REPLACE INTO stories VALUES(?, ?, ?, ?)"); state.requery(); state.bindLong(1, dialogId); @@ -464,21 +406,11 @@ private void updateStoryItemInternal(long dialogId, TLRPC.StoryItem storyItem) { 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); - } NativeByteBuffer nativeByteBuffer = StoryCustomParamsHelper.writeLocalParams(storyItem); if (nativeByteBuffer != null) { - state.bindByteBuffer(6, nativeByteBuffer); + state.bindByteBuffer(4, nativeByteBuffer); } else { - state.bindNull(6); + state.bindNull(4); } if (nativeByteBuffer != null) { nativeByteBuffer.reuse(); @@ -516,16 +448,12 @@ public void processUpdate(TLRPC.TL_updateStory updateStory) { 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, custom_params FROM stories WHERE dialog_id = %d AND story_id = %d", dialogId, storyId)); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, custom_params 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); - NativeByteBuffer customData = cursor.byteBufferValue(3); + NativeByteBuffer customData = cursor.byteBufferValue(1); if (data != null) { TLRPC.StoryItem storyItem = TLRPC.StoryItem.TLdeserialize(data, data.readInt32(true), true); - storyItem.attachPath = path; - storyItem.firstFramePath = thumbPath; StoryCustomParamsHelper.readLocalParams(storyItem, customData); data.reuse(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryMediaAreasView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryMediaAreasView.java index a614745229..b742d6c4ba 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryMediaAreasView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryMediaAreasView.java @@ -40,6 +40,8 @@ import org.telegram.ui.Components.ColoredImageSpan; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.ScaleStateListAnimator; +import org.telegram.ui.EmojiAnimationsOverlay; import org.telegram.ui.LocationActivity; import org.telegram.ui.Stories.recorder.HintView2; @@ -53,12 +55,15 @@ public class StoryMediaAreasView extends FrameLayout implements View.OnClickList private final FrameLayout hintsContainer; private boolean malicious; + Matrix matrix = new Matrix(); + float[] point = new float[2]; + private Theme.ResourcesProvider resourcesProvider; public StoryMediaAreasView(Context context, Theme.ResourcesProvider resourcesProvider) { super(context); this.resourcesProvider = resourcesProvider; - + setClipChildren(false); addView(hintsContainer = new FrameLayout(context)); } @@ -156,7 +161,12 @@ public void onClick(View v) { onHintVisible(false); }, 200); - LocationActivity fragment = new LocationActivity(3); + LocationActivity fragment = new LocationActivity(3) { + @Override + protected boolean disablePermissionCheck() { + return true; + } + }; fragment.setResourceProvider(resourcesProvider); TLRPC.TL_message message = new TLRPC.TL_message(); if (selectedArea.mediaArea instanceof TLRPC.TL_mediaAreaVenue) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryReactionWidgetBackground.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryReactionWidgetBackground.java new file mode 100644 index 0000000000..de23f2e8d4 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryReactionWidgetBackground.java @@ -0,0 +1,165 @@ +package org.telegram.ui.Stories; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.drawable.Drawable; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.graphics.ColorUtils; + +import org.checkerframework.checker.units.qual.A; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.ui.ActionBar.ActionBarPopupWindow; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.CubicBezierInterpolator; + +public class StoryReactionWidgetBackground extends Drawable { + + private final int STYLE_FILLED = 0; + private final int STYLE_TRANSCLUENT = 1; + int style; + private final View parent; + Paint shadowPaint; + Paint backgroundPaint; + int alpha = 255; + + float[] points = new float[3 * 5]; + AnimatedFloat progressToMirrored; + private boolean mirror; + private Paint xRefPaint; + Path path = new Path(); + + + public StoryReactionWidgetBackground(View parent) { + this.parent = parent; + progressToMirrored = new AnimatedFloat(parent, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + shadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + shadowPaint.setShadowLayer(AndroidUtilities.dp(4), 0, 0, 0x5f000000); + + backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + backgroundPaint.setColor(Color.WHITE); + } + + public void updateShadowLayer(float scale) { + shadowPaint.setShadowLayer(AndroidUtilities.dp(2) / scale, 0, AndroidUtilities.dpf2(0.7f) / scale, ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * 0.18f))); + } + + @Override + public void draw(@NonNull Canvas canvas) { + points[0] = getBounds().centerX(); + points[1] = getBounds().centerY(); + points[2] = getBounds().height() / 2f; + + points[3] = getBounds().left + getBounds().width() * 1.027f; + points[4] = getBounds().top + getBounds().height() * 0.956f; + points[5] = getBounds().height() * 0.055f; + + points[6] = getBounds().left + getBounds().width() * 0.843f; + points[7] = getBounds().top + getBounds().height() * 0.812f; + points[8] = getBounds().height() * 0.132f; + + //mirrored + points[9] = getBounds().left + getBounds().width() * (1f - 1.027f); + points[10] = getBounds().top + getBounds().height() * 0.956f; + points[11] = getBounds().height() * 0.055f; + + points[12] = getBounds().left + getBounds().width() * (1f - 0.843f); + points[13] = getBounds().top + getBounds().height() * 0.812f; + points[14] = getBounds().height() * 0.132f; + + float mirrorProgress = progressToMirrored.set(mirror ? 1f : 0); + if (style == STYLE_FILLED) { + backgroundPaint.setColor(Color.WHITE); + } else if (style == STYLE_TRANSCLUENT) { + if (xRefPaint == null) { + xRefPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + xRefPaint.setColor(0xff000000); + xRefPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + xRefPaint.setStrokeWidth(AndroidUtilities.dp(3)); + } + backgroundPaint.setColor(ColorUtils.setAlphaComponent(Color.BLACK, 127)); + } + if (alpha != 255 || style == STYLE_TRANSCLUENT) { + canvas.saveLayerAlpha(getBounds().left - getBounds().width() * 0.2f, getBounds().top, getBounds().right + getBounds().width() * 0.2f, getBounds().bottom + getBounds().height() * 0.2f, alpha, Canvas.ALL_SAVE_FLAG); + } else { + canvas.save(); + } + path.rewind(); + for (int k = 0; k < 2; k++) { + if (style == STYLE_TRANSCLUENT && k == 0) { + continue; + } + Paint paint = k == 0 ? shadowPaint : backgroundPaint; + int shadowOffset = k == 0 ? 1 : 0; + for (int i = 0; i < 5; i++) { + if (i == 1 || i == 2) { + if (mirrorProgress == 1f) { + continue; + } + path.addCircle(points[i * 3], points[i * 3 + 1], points[i * 3 + 2] * (1f - mirrorProgress) - shadowOffset, Path.Direction.CW); + // drawCircle(canvas, , paint); + } else if (i == 3 || i == 4) { + if (mirrorProgress == 0) { + continue; + } + path.addCircle(points[i * 3], points[i * 3 + 1], points[i * 3 + 2] * mirrorProgress - shadowOffset, Path.Direction.CW); + //drawCircle(canvas, points[i * 3], points[i * 3 + 1], points[i * 3 + 2] * mirrorProgress - shadowOffset, paint); + } else { + path.addCircle(points[i * 3], points[i * 3 + 1], points[i * 3 + 2] - shadowOffset, Path.Direction.CW); + // drawCircle(canvas, points[i * 3], points[i * 3 + 1], points[i * 3 + 2] - shadowOffset, paint); + } + } + canvas.drawPath(path, paint); + } + canvas.restore(); + } + + private void drawCircle(Canvas canvas, float cx, float cy, float r, Paint paint) { + if (style == STYLE_TRANSCLUENT) { + canvas.drawCircle(cx, cy, r, xRefPaint); + } + canvas.drawCircle(cx, cy, r, paint); + } + + @Override + public void setAlpha(int alpha) { + this.alpha = alpha; + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + + } + + @Override + public int getOpacity() { + return 0; + } + + public void setMirror(boolean mirror, boolean animate) { + this.mirror = mirror; + if (!animate) { + progressToMirrored.set(mirror ? 1f : 0, true); + } else { + parent.invalidate(); + } + } + + public void nextStyle() { + style++; + if (style >= 2) { + style = 0; + } + } + + public boolean isDarkStyle() { + return style == STYLE_TRANSCLUENT; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryViewer.java index 1fe1732b8c..52c7a32c8a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryViewer.java @@ -279,7 +279,7 @@ public void open(Context context, long dialogId, PlaceProvider placeProvider) { public void open(Context context, int startStoryId, StoriesController.StoriesList storiesList, PlaceProvider placeProvider) { currentAccount = UserConfig.selectedAccount; ArrayList peerIds = new ArrayList<>(); - peerIds.add(storiesList.userId); + peerIds.add(storiesList.dialogId); dayStoryId = startStoryId; open(context, null, peerIds, 0, storiesList, null, placeProvider, false); } @@ -298,7 +298,7 @@ public void open(Context context, TLRPC.TL_userStories userStories, PlaceProvide 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); + peerIds.add(storiesList.dialogId); dayStoryId = startStoryId; open(context, storyItem, peerIds, 0, storiesList, null, placeProvider, reversed); } @@ -1178,7 +1178,7 @@ public void switchToNextAndRemoveCurrentPeer() { close(false); } else { storiesViewPager.onNextIdle(() -> { - storiesViewPager.setDays(storiesList.userId, newDays, currentAccount); + storiesViewPager.setDays(storiesList.dialogId, newDays, currentAccount); }); } } else { @@ -1479,7 +1479,7 @@ public void invalidate() { updateTransitionParams(); } if (storiesList != null) { - storiesViewPager.setDays(storiesList.userId, storiesList.getDays(), currentAccount); + storiesViewPager.setDays(storiesList.dialogId, storiesList.getDays(), currentAccount); } else { storiesViewPager.setPeerIds(peerIds, currentAccount, position); } @@ -2448,7 +2448,7 @@ public void didReceivedNotification(int id, int account, Object... args) { StoriesController.StoriesList list = (StoriesController.StoriesList) args[0]; if (storiesList == list) { PeerStoriesView peerStoriesView = getCurrentPeerView(); - storiesViewPager.setDays(storiesList.userId, storiesList.getDays(), currentAccount); + storiesViewPager.setDays(storiesList.dialogId, storiesList.getDays(), currentAccount); if (selfStoryViewsView != null) { TLRPC.StoryItem currentSelectedStory = selfStoryViewsView.getSelectedStory(); ArrayList storyItems = new ArrayList<>(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/UserListPoller.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/UserListPoller.java index b77e4fe71c..47753d2c38 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/UserListPoller.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/UserListPoller.java @@ -7,6 +7,7 @@ import org.checkerframework.checker.units.qual.A; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ChatObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.MessagesStorage; import org.telegram.messenger.NotificationCenter; @@ -17,6 +18,7 @@ import org.telegram.tgnet.TLRPC; import org.telegram.ui.Cells.DialogCell; import org.telegram.ui.Cells.UserCell; +import org.telegram.ui.Components.Paint.Path; import org.telegram.ui.Components.RecyclerListView; import java.util.ArrayList; @@ -58,6 +60,7 @@ public void run() { if (response != null) { TLRPC.Vector vector = (TLRPC.Vector) response; ArrayList usersToUpdate = new ArrayList<>(); + ArrayList chatsToUpdate = new ArrayList<>(); for (int i = 0; i < vector.objects.size(); i++) { TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogsFinal.get(i)); if (user == null) { @@ -100,6 +103,15 @@ public void checkList(RecyclerListView recyclerListView) { dialogIds.add(dialogId); } } + } else { + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-dialogId); + if (ChatObject.isChannel(chat)) { + long lastPollTime = userPollLastTime.get(dialogId, 0); + if (currentTime - lastPollTime > 60 * 60 * 1000) { + userPollLastTime.put(dialogId, currentTime); + dialogIds.add(dialogId); + } + } } } if (!dialogIds.isEmpty()) { 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 index 9b9c4ff9e3..edcf47e0a3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/ButtonWithCounterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/ButtonWithCounterView.java @@ -85,6 +85,42 @@ public ButtonWithCounterView(Context context, boolean filled, Theme.ResourcesPro setWillNotDraw(false); } + private boolean countFilled = true; + public void setCountFilled(boolean filled) { + countFilled = filled; + countText.setTextSize(dp(countFilled ? 12 : 14)); + countText.setTextColor( + countFilled ? + Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider) : + text.getTextColor() + ); + } + + private int timerSeconds = 0; + private Runnable tick; + public void setTimer(int seconds, Runnable whenTimerUp) { + AndroidUtilities.cancelRunOnUIThread(tick); + + setCountFilled(false); + setCount(timerSeconds = seconds, false); + setShowZero(false); + AndroidUtilities.runOnUIThread(tick = () -> { + timerSeconds--; + setCount(timerSeconds, true); + if (timerSeconds > 0) { + AndroidUtilities.runOnUIThread(tick, 1000); + } else { + setClickable(true); + if (whenTimerUp != null) { + whenTimerUp.run(); + } + } + }, 1000); + } + public boolean isTimerActive() { + return timerSeconds > 0; + } + public void setText(CharSequence newText, boolean animated) { if (animated) { text.cancelAnimation(); @@ -255,9 +291,9 @@ protected void onDraw(Canvas canvas) { text.draw(canvas); AndroidUtilities.rectTmp2.set( - (int) ((getMeasuredWidth() - width) / 2f + textWidth + dp(5f)), + (int) ((getMeasuredWidth() - width) / 2f + textWidth + dp(countFilled ? 5 : 2)), (int) ((getMeasuredHeight() - dp(18)) / 2f), - (int) ((getMeasuredWidth() - width) / 2f + textWidth + dp(5f + 4 + 4) + Math.max(dp(9), countText.getCurrentWidth())), + (int) ((getMeasuredWidth() - width) / 2f + textWidth + dp((countFilled ? 5 : 2) + 4 + 4) + Math.max(dp(9), countText.getCurrentWidth())), (int) ((getMeasuredHeight() + dp(18)) / 2f) ); AndroidUtilities.rectTmp.set(AndroidUtilities.rectTmp2); @@ -266,11 +302,13 @@ protected void onDraw(Canvas canvas) { canvas.save(); canvas.scale(countScale, countScale, AndroidUtilities.rectTmp2.centerX(), AndroidUtilities.rectTmp2.centerY()); } - paint.setAlpha((int) (globalAlpha * (1f - loadingT) * countAlpha * countAlpha * AndroidUtilities.lerp(.5f, 1f, enabledT))); - canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(10), dp(10), paint); + if (countFilled) { + paint.setAlpha((int) (globalAlpha * (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) (globalAlpha * (1f - loadingT) * countAlpha)); + countText.setAlpha((int) (globalAlpha * (1f - loadingT) * countAlpha * (countFilled ? 1 : .5f))); countText.setBounds(AndroidUtilities.rectTmp2); countText.draw(canvas); if (countScale != 1) { 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 index 15049911bc..d113056707 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/CaptionContainerView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/CaptionContainerView.java @@ -1,12 +1,11 @@ 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 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; @@ -15,45 +14,31 @@ 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; @@ -64,72 +49,94 @@ 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.AnimatedEmojiDrawable; import org.telegram.ui.Components.AnimatedFloat; import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.BlurringShader; import org.telegram.ui.Components.ButtonBounce; +import org.telegram.ui.Components.CaptionPhotoViewer; 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.ScaleStateListAnimator; import org.telegram.ui.Components.SizeNotifierFrameLayout; import org.telegram.ui.LaunchActivity; -import org.telegram.ui.Stories.PeerStoriesView; -import org.telegram.ui.WrappedResourceProvider; +import org.telegram.ui.Stories.DarkThemeResourceProvider; public class CaptionContainerView extends FrameLayout { - private final Theme.ResourcesProvider resourcesProvider; + protected Theme.ResourcesProvider resourcesProvider; private final FrameLayout containerView; - private int currentAccount; private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); public final EditTextEmoji editText; + private CombinedDrawable applyButtonDrawable; public ImageView applyButton; + public FrameLayout limitTextContainer; 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 int codePointCount; 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; + private final TextPaint hintTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + private Bitmap hintTextBitmap; + private final Paint hintTextBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + + private final FrameLayout rootView; + private final SizeNotifierFrameLayout sizeNotifierFrameLayout; public final KeyboardNotifier keyboardNotifier; public MentionsContainerView mentionContainer; private int shiftDp = -4; + private final BlurringShader.BlurManager blurManager; + + private final BlurringShader.StoryBlurDrawer captionBlur; + private final BlurringShader.StoryBlurDrawer backgroundBlur; + private BlurringShader.StoryBlurDrawer mentionBackgroundBlur; + + protected int currentAccount = UserConfig.selectedAccount; + public void setAccount(int currentAccount) { + this.currentAccount = currentAccount; + } + + private boolean ignoreTextChange; - public CaptionContainerView(Context context, int currentAccount, StoryRecorder.WindowView rootView, FrameLayout containerView, Theme.ResourcesProvider resourcesProvider) { + protected int getEditTextStyle() { + return EditTextEmoji.STYLE_STORY; + } + + boolean waitingForScrollYChange; + int beforeScrollY; + int goingToScrollY; + + public CaptionContainerView(Context context, FrameLayout rootView, SizeNotifierFrameLayout sizeNotifierFrameLayout, FrameLayout containerView, Theme.ResourcesProvider resourcesProvider, BlurringShader.BlurManager blurManager) { super(context); this.resourcesProvider = resourcesProvider; - this.currentAccount = currentAccount; this.rootView = rootView; + this.sizeNotifierFrameLayout = sizeNotifierFrameLayout; this.containerView = containerView; + this.blurManager = blurManager; + + backgroundBlur = new BlurringShader.StoryBlurDrawer(blurManager, this, BlurringShader.StoryBlurDrawer.BLUR_TYPE_BACKGROUND, !customBlur()); backgroundPaint.setColor(0x80000000); keyboardNotifier = new KeyboardNotifier(rootView, this::updateKeyboard); - editText = new EditTextEmoji(context, rootView, null, EditTextEmoji.STYLE_STORY, true, resourcesProvider) { + editText = new EditTextEmoji(context, sizeNotifierFrameLayout, null, getEditTextStyle(), true, new DarkThemeResourceProvider()) { @Override protected void onEmojiKeyboardUpdate() { keyboardNotifier.fire(); @@ -144,26 +151,84 @@ protected void onWaitingForKeyboard() { protected void createEmojiView() { super.createEmojiView(); EmojiView emojiView = getEmojiView(); - if (emojiView != null) { + if (emojiView != null && getEditTextStyle() == EditTextEmoji.STYLE_STORY) { emojiView.shouldLightenBackground = false; emojiView.fixBottomTabContainerTranslation = false; emojiView.setShouldDrawBackground(false); + if (CaptionContainerView.this instanceof CaptionPhotoViewer) { + emojiView.setPadding(0, 0, 0, AndroidUtilities.navigationBarHeight); + emojiView.emojiCacheType = AnimatedEmojiDrawable.CACHE_TYPE_ALERT_PREVIEW; + } } } + private BlurringShader.StoryBlurDrawer blurDrawer; + @Override protected void drawEmojiBackground(Canvas canvas, View view) { - AndroidUtilities.rectTmp.set(0, 0, view.getWidth(), view.getHeight()); - drawBackground(canvas, AndroidUtilities.rectTmp, 0, .95f, view); + rectF.set(0, 0, view.getWidth(), view.getHeight()); + if (customBlur()) { + if (blurDrawer == null) { + blurDrawer = new BlurringShader.StoryBlurDrawer(blurManager, view, BlurringShader.StoryBlurDrawer.BLUR_TYPE_EMOJI_VIEW); + } + drawBlur(blurDrawer, canvas, rectF, 0, false, 0, -view.getY(), false); + } else { + drawBackground(canvas, rectF, 0, .95f, view); + } + } + + @Override + protected boolean onScrollYChange(int afterScrollY) { + if (scrollAnimator != null && scrollAnimator.isRunning() && afterScrollY == goingToScrollY) { + return false; + } + CaptionContainerView.this.invalidate(); + if (waitingForScrollYChange) { + waitingForScrollYChange = false; + if (beforeScrollY != afterScrollY && (scrollAnimator == null || !scrollAnimator.isRunning() || afterScrollY != goingToScrollY)) { + if (scrollAnimator != null) { + scrollAnimator.cancel(); + } + editText.getEditText().setScrollY(beforeScrollY); + scrollAnimator = ObjectAnimator.ofInt(editText.getEditText(), "scrollY", beforeScrollY, goingToScrollY = afterScrollY); + scrollAnimator.setDuration(240); + scrollAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + scrollAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (scrollAnimator != animation) { + return; + } + scrollAnimator = null; + editText.getEditText().setScrollY(goingToScrollY); + } + }); + scrollAnimator.start(); + return false; + } + } + return true; } }; - editText.setHint(LocaleController.getString("StoryAddCaption", R.string.StoryAddCaption)); + editText.getEditText().setShadowLayer(dp(10), 0, 0, 0); + editText.setFocusable(true); + editText.setFocusableInTouchMode(true); + editText.getEditText().hintLayoutYFix = true; + editText.getEditText().drawHint = this::drawHint; + captionBlur = new BlurringShader.StoryBlurDrawer(blurManager, editText.getEditText(), customBlur() ? BlurringShader.StoryBlurDrawer.BLUR_TYPE_CAPTION : BlurringShader.StoryBlurDrawer.BLUR_TYPE_CAPTION_XFER); + editText.getEditText().setHintColor(0x80ffffff); + editText.getEditText().setHintText(LocaleController.getString("AddCaption", R.string.AddCaption), false); + hintTextBitmapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); 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) { - + if (scrollAnimator == null || !scrollAnimator.isRunning()) { + beforeScrollY = editText.getEditText().getScrollY(); + waitingForScrollYChange = true; + } } @Override @@ -175,7 +240,7 @@ public void onTextChanged(CharSequence text, int start, int before, int count) { createMentionsContainer(); } if (mentionContainer.getAdapter() != null) { - mentionContainer.getAdapter().setUserOrChar(UserConfig.getInstance(currentAccount).getCurrentUser(), null); +// mentionContainer.getAdapter().setUserOrChat(UserConfig.getInstance(currentAccount).getCurrentUser(), null); mentionContainer.getAdapter().searchUsernameOrHashtag(text, editText.getEditText().getSelectionStart(), null, false, false); } } @@ -185,51 +250,53 @@ public void onTextChanged(CharSequence text, int start, int before, int count) { @Override public void afterTextChanged(Editable s) { - EditTextCaption editText2 = editText.getEditText(); - if (editText2 != null && editText2.getLayout() != null) { - editText2.ignoreClipTop = ( - editText2.getLayout().getHeight() > (dp(120) - editText2.getPaddingTop() - editText2.getPaddingBottom()) - ); - } - int length = 0; - try { - length = editText.getEditText().getText().length(); - } catch (Exception ignore) { - } + codePointCount = Character.codePointCount(s, 0, s.length()); String limitText = null; - final boolean premium = UserConfig.getInstance(currentAccount).isPremium(); - final int limit = premium ? MessagesController.getInstance(currentAccount).storyCaptionLengthLimitPremium : MessagesController.getInstance(currentAccount).storyCaptionLengthLimitDefault; - if (length + 25 > limit) { - limitText = "" + (limit - length); + final int limit = getCaptionLimit(); + if (codePointCount + 25 > limit) { + limitText = "" + (limit - codePointCount); } limitTextView.cancelAnimation(); limitTextView.setText(limitText); - limitTextView.setTextColor(length >= limit ? 0xffEC7777 : 0xffffffff); - if (length > limit && !premium && length < MessagesController.getInstance(currentAccount).storyCaptionLengthLimitPremium && length > lastLength && (captionLimitToast() || MessagesController.getInstance(currentAccount).premiumLocked)) { + limitTextView.setTextColor(codePointCount >= limit ? 0xffEC7777 : 0xffffffff); + if (codePointCount > limit && !UserConfig.getInstance(currentAccount).isPremium() && codePointCount < getCaptionPremiumLimit() && codePointCount > lastLength && (captionLimitToast() || MessagesController.getInstance(currentAccount).premiumLocked)) { AndroidUtilities.shakeViewSpring(limitTextView, shiftDp = -shiftDp); BotWebViewVibrationEffect.APP_ERROR.vibrate(); } - lastLength = length; + lastLength = codePointCount; - final boolean overLimit = length > limit; + final boolean overLimit = codePointCount > limit; if (overLimit != lastOverLimit) { onCaptionLimitUpdate(overLimit); } lastOverLimit = overLimit; + + if (!ignoreTextChange) { + AndroidUtilities.cancelRunOnUIThread(textChangeRunnable); + AndroidUtilities.runOnUIThread(textChangeRunnable, 1500); + } + ignoreTextChange = false; + + AndroidUtilities.runOnUIThread(() -> { + waitingForScrollYChange = false; + }); } }); editText.getEditText().setLinkTextColor(Color.WHITE); 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); + ScaleStateListAnimator.apply(applyButton, 0.05f, 1.25f); + applyButtonDrawable = new CombinedDrawable(Theme.createCircleDrawable(AndroidUtilities.dp(16), Theme.getColor(Theme.key_dialogFloatingButton, resourcesProvider)), context.getResources().getDrawable(R.drawable.input_done).mutate(), 0, AndroidUtilities.dp(1)); + applyButtonDrawable.setCustomSize(AndroidUtilities.dp(32), AndroidUtilities.dp(32)); + applyButton.setImageDrawable(applyButtonDrawable); applyButton.setScaleType(ImageView.ScaleType.CENTER); applyButton.setAlpha(0f); applyButton.setVisibility(View.GONE); applyButton.setOnClickListener(e -> { closeKeyboard(); + AndroidUtilities.cancelRunOnUIThread(textChangeRunnable); + textChangeRunnable.run(); }); applyButton.setTranslationY(-AndroidUtilities.dp(1)); addView(applyButton, LayoutHelper.createFrame(44, 44, Gravity.RIGHT | Gravity.BOTTOM)); @@ -240,55 +307,33 @@ public void afterTextChanged(Editable s) { 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, 50)); + limitTextContainer = new FrameLayout(context); + limitTextContainer.setTranslationX(dp(2)); + limitTextContainer.addView(limitTextView, LayoutHelper.createFrame(52, 16, Gravity.RIGHT | Gravity.BOTTOM)); + addView(limitTextContainer, LayoutHelper.createFrame(52, 16, Gravity.RIGHT | Gravity.BOTTOM, 0, 0, 0, 50)); 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); - } - }; + private final Runnable textChangeRunnable = () -> onTextChange(); + protected void onTextChange() {} - final boolean isPremium = UserConfig.getInstance(currentAccount).isPremium(); + public void invalidateBlur() { + invalidate(); + editText.getEditText().invalidate(); + editText.getEmojiButton().invalidate(); + if (mentionContainer != null) { + mentionContainer.invalidate(); + } + if (editText.getEmojiView() != null && customBlur()) { + editText.getEmojiView().invalidate(); + } + } - 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(); - 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)); + private Utilities.CallbackVoidReturn getUiBlurBitmap; + public void setUiBlurBitmap(Utilities.CallbackVoidReturn get) { + getUiBlurBitmap = get; } public void closeKeyboard() { @@ -298,26 +343,77 @@ public void closeKeyboard() { public boolean ignoreTouches; + protected boolean ignoreTouches() { + return false; + } + @Override public boolean dispatchTouchEvent(MotionEvent ev) { - if (ignoreTouches) { + if (ignoreTouches || ignoreTouches() || !bounds.contains(ev.getX(), ev.getY()) && !keyboardShown) { return false; } + if (ev.getAction() == MotionEvent.ACTION_DOWN && !keyboardShown) { + for (int i = 0; i < getChildCount(); ++i) { + View child = getChildAt(i); + if (child == null || !child.isClickable() || child.getVisibility() != View.VISIBLE || child.getAlpha() < .5f || editText == child) { + continue; + } + rectF.set(child.getX(), child.getY(), child.getX() + child.getWidth(), child.getY() + child.getHeight()); + if (rectF.contains(ev.getX(), ev.getY())) { + return super.dispatchTouchEvent(ev); + } + } + editText.getEditText().requestFocus(); + editText.getEditText().setSelection(editText.getEditText().length(), editText.getEditText().length()); + editText.openKeyboard(); + editText.getEditText().setScrollY(0); + return true; + } return super.dispatchTouchEvent(ev); } + private ObjectAnimator scrollAnimator; + private void animateScrollTo(boolean end) { + final EditTextCaption et = editText.getEditText(); + if (et == null || et.getLayout() == null) { + return; + } + if (scrollAnimator != null) { + scrollAnimator.cancel(); + } + int sy = et.getScrollY(); + editText.setSelection(end ? editText.length() : 0); + int totalLineHeight = et.getLayout().getLineTop(et.getLineCount()); + int visibleHeight = et.getHeight() - et.getPaddingTop() - et.getPaddingBottom(); + int nsy = end ? totalLineHeight - visibleHeight : 0; + scrollAnimator = ObjectAnimator.ofInt(et, "scrollY", sy, nsy); + scrollAnimator.setDuration(360); + scrollAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + scrollAnimator.start(); + } + private void createMentionsContainer() { - mentionContainer = new MentionsContainerView(getContext(), UserConfig.getInstance(currentAccount).getClientUserId(), 0, LaunchActivity.getLastFragment(), null, resourcesProvider) { + mentionContainer = new MentionsContainerView(getContext(), UserConfig.getInstance(currentAccount).getClientUserId(), 0, LaunchActivity.getLastFragment(), null, new DarkThemeResourceProvider()) { @Override public void drawRoundRect(Canvas canvas, Rect rectTmp, float radius) { - AndroidUtilities.rectTmp.set(rectTmp); - drawBackground(canvas, AndroidUtilities.rectTmp, radius, .9f, mentionContainer); + rectF.set(rectTmp); + if (customBlur()) { + drawBlur(mentionBackgroundBlur, canvas, rectF, radius, false, -mentionContainer.getX(), -mentionContainer.getY(), false); + } else { + Paint blurPaint = mentionBackgroundBlur.getPaint(1f); + if (blurPaint == null) { + CaptionContainerView.this.backgroundPaint.setAlpha(0x80); + canvas.drawRoundRect(rectF, radius, radius, CaptionContainerView.this.backgroundPaint); + } else { + canvas.drawRoundRect(rectF, radius, radius, blurPaint); + CaptionContainerView.this.backgroundPaint.setAlpha(0x50); + canvas.drawRoundRect(rectF, radius, radius, CaptionContainerView.this.backgroundPaint); + } + } } }; - mentionContainer.getAdapter().setAllowStickers(false); - mentionContainer.getAdapter().setAllowBots(false); - mentionContainer.getAdapter().setAllowChats(false); - mentionContainer.getAdapter().setSearchInDailogs(true); + mentionBackgroundBlur = new BlurringShader.StoryBlurDrawer(blurManager, mentionContainer, BlurringShader.StoryBlurDrawer.BLUR_TYPE_BACKGROUND); + setupMentionContainer(); mentionContainer.withDelegate(new MentionsContainerView.Delegate() { @Override @@ -333,6 +429,13 @@ public Paint.FontMetricsInt getFontMetrics() { containerView.addView(mentionContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.BOTTOM)); } + protected void setupMentionContainer() { + mentionContainer.getAdapter().setAllowStickers(false); + mentionContainer.getAdapter().setAllowBots(false); + mentionContainer.getAdapter().setAllowChats(false); + mentionContainer.getAdapter().setSearchInDailogs(true); + } + private void replaceWithText(int start, int len, CharSequence text, boolean parseEmoji) { if (editText == null) { return; @@ -350,45 +453,10 @@ private void replaceWithText(int start, int len, CharSequence text, boolean pars } } - 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(); } @@ -398,36 +466,12 @@ 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() { + return (int) heightAnimated.get(); } - public int getEditTextHeight() { - int height = editText.getHeight(); - if (keyboardShown) { - height = Math.max(dp(46), height); - } else { - height = Math.min(dp(150), height); - } - return height; + public int getEditTextHeightClosedKeyboard() { + return Math.min(dp(82), editText.getHeight()); } private Utilities.Callback onKeyboardOpen; @@ -437,12 +481,20 @@ public void setOnKeyboardOpen(Utilities.Callback onKeyboardOpen) { ObjectAnimator parentKeyboardAnimator; + protected int additionalKeyboardHeight() { + return AndroidUtilities.navigationBarHeight; + } + private void updateKeyboard(int keyboardHeight) { - rootView.notifyHeightChanged(); - if (editText.isPopupShowing() || editText.isWaitingForKeyboardOpen()) { - keyboardHeight = Math.max(0, AndroidUtilities.navigationBarHeight + editText.getKeyboardHeight()); + if (sizeNotifierFrameLayout != null) { + sizeNotifierFrameLayout.notifyHeightChanged(); + } + if (editText.isPopupShowing()) { + keyboardHeight = Math.max(0, additionalKeyboardHeight() + editText.getEmojiPadding()); + } else if (editText.isWaitingForKeyboardOpen()) { + keyboardHeight = Math.max(0, additionalKeyboardHeight() + editText.getKeyboardHeight()); } - keyboardHeight = Math.max(0, keyboardHeight - rootView.getBottomPadding(true)); + keyboardHeight = Math.max(0, keyboardHeight - (sizeNotifierFrameLayout == null ? 0 : sizeNotifierFrameLayout.getBottomPadding())); View parent = (View) getParent(); parent.clearAnimation(); @@ -476,6 +528,14 @@ private void updateKeyboard(int keyboardHeight) { updateShowKeyboard(toKeyboardShow, true); }; + protected int getEditTextLeft() { + return 0; + } + + protected void updateEditTextLeft() { + editText.getEditText().setTranslationX(lerp(dp(-40 + 18) + getEditTextLeft(), dp(2), keyboardT)); + } + public float keyboardT; public boolean keyboardShown; private ValueAnimator keyboardAnimator; @@ -491,6 +551,7 @@ private void updateShowKeyboard(boolean show, boolean animated) { if (onKeyboardOpen != null) { onKeyboardOpen.run(show); } + beforeUpdateShownKeyboard(show); if (animated) { if (show) { if (mentionContainer != null) { @@ -499,24 +560,27 @@ private void updateShowKeyboard(boolean show, boolean animated) { 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(lerp(dp(-40 + 18), dp(2), keyboardT)); + editText.getEditText().setTranslationX(lerp(dp(-40 + 18) + getEditTextLeft(), dp(2), keyboardT)); editText.setTranslationX(lerp(0, dp(-8), keyboardT)); - editText.setTranslationY(lerp(0, dp(12 - 2), keyboardT)); - limitTextView.setTranslationX(lerp(-dp(8), dp(2), keyboardT)); - limitTextView.setTranslationY(lerp(-dp(8), 0, keyboardT)); + editText.setTranslationY(lerp(0, dp(10), keyboardT)); + limitTextContainer.setTranslationX(lerp(-dp(8), dp(2), keyboardT)); + limitTextContainer.setTranslationY(lerp(-dp(8), 0, keyboardT)); editText.getEmojiButton().setAlpha(keyboardT); applyButton.setAlpha((float) Math.pow(keyboardT, 16)); - periodButton.setAlpha(1f - keyboardT); + onUpdateShowKeyboard(keyboardT); if (mentionContainer != null) { mentionContainer.setAlpha((float) Math.pow(keyboardT, 4)); } + editText.getEditText().invalidate(); invalidate(); }); + if (!show) { + editText.getEditText().setAllowDrawCursor(false); + } keyboardAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -525,9 +589,11 @@ public void onAnimationEnd(Animator animation) { if (mentionContainer != null) { mentionContainer.setVisibility(View.GONE); } - } else { - periodButton.setVisibility(View.GONE); } + if (show) { + editText.getEditText().setAllowDrawCursor(true); + } + afterUpdateShownKeyboard(show); } }); if (show) { @@ -540,18 +606,20 @@ public void onAnimationEnd(Animator animation) { keyboardAnimator.start(); } else { keyboardT = show ? 1 : 0; - editText.getEditText().setTranslationX(lerp(AndroidUtilities.dp(-40 + 18), AndroidUtilities.dp(2), keyboardT)); + editText.getEditText().setTranslationX(lerp(AndroidUtilities.dp(-40 + 18) + getEditTextLeft(), AndroidUtilities.dp(2), keyboardT)); editText.setTranslationX(lerp(0, AndroidUtilities.dp(-8), keyboardT)); - editText.setTranslationY(lerp(0, AndroidUtilities.dp(12 - 2), keyboardT)); - limitTextView.setTranslationX(lerp(-dp(8), dp(2), keyboardT)); - limitTextView.setTranslationY(lerp(-dp(8), 0, keyboardT)); + editText.setTranslationY(lerp(0, AndroidUtilities.dp(10), keyboardT)); + limitTextContainer.setTranslationX(lerp(-dp(8), dp(2), keyboardT)); + limitTextContainer.setTranslationY(lerp(-dp(8), 0, 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); + onUpdateShowKeyboard(keyboardT); + editText.getEditText().setAllowDrawCursor(show); + afterUpdateShownKeyboard(show); invalidate(); } + animateScrollTo(show); editText.setSuggestionsEnabled(show); if (!show) { editText.getEditText().setSpoilersRevealed(false, true); @@ -564,6 +632,10 @@ public void onAnimationEnd(Animator animation) { ignoreDraw = true; drawBlurBitmap(blurBitmap, 12); ignoreDraw = false; + if (blurBitmap != null && blurBitmap.isRecycled()) { + blurBitmap = null; + return; + } blurBitmapShader = new BitmapShader(blurBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); if (blurBitmapMatrix == null) { blurBitmapMatrix = new Matrix(); @@ -579,15 +651,36 @@ public void onAnimationEnd(Animator animation) { } } + protected void onUpdateShowKeyboard(float keyboardT) { + + } + + protected void beforeUpdateShownKeyboard(boolean show) { + + } + + protected void afterUpdateShownKeyboard(boolean show) { + + } + + public int getCodePointCount() { + return codePointCount; + } + public boolean isCaptionOverLimit() { - int length = 0; - try { - length = editText.getEditText().getText().length(); - } catch (Exception ignore) { - } - final boolean premium = UserConfig.getInstance(currentAccount).isPremium(); - final int limit = premium ? MessagesController.getInstance(currentAccount).storyCaptionLengthLimitPremium : MessagesController.getInstance(currentAccount).storyCaptionLengthLimitDefault; - return length > limit; + return getCodePointCount() > getCaptionLimit(); + } + + protected int getCaptionLimit() { + return UserConfig.getInstance(currentAccount).isPremium() ? getCaptionPremiumLimit() : getCaptionDefaultLimit(); + } + + protected int getCaptionDefaultLimit() { + return 0; + } + + protected int getCaptionPremiumLimit() { + return 0; } protected void onCaptionLimitUpdate(boolean overLimit) { @@ -603,16 +696,6 @@ protected void drawBlurBitmap(Bitmap bitmap, float amount) { 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; @@ -638,6 +721,11 @@ public boolean onBackPressed() { private boolean ignoreDraw = false; + private final RectF rectF = new RectF(); + private final RectF bounds = new RectF(); + + protected void onEditHeightChange(int height) {} + @Override protected void dispatchDraw(Canvas canvas) { if (ignoreDraw) { @@ -647,24 +735,25 @@ protected void dispatchDraw(Canvas canvas) { if (keyboardShown) { height = Math.max(dp(46), height); } else { - height = Math.min(dp(150), height); + height = Math.min(dp(82), height); } - if (height != lastHeight) { + final int heightAnimated = (int) this.heightAnimated.set(height); + if (heightAnimated != lastHeight) { + onEditHeightChange(heightAnimated); if (onHeightUpdate != null) { - onHeightUpdate.run(height); + onHeightUpdate.run(heightAnimated); } lastHeight = height; } - final int heightAnimated = (int) this.heightAnimated.set(height); updateMentionsLayoutPosition(); - final float heightTranslation = height - heightAnimated; + final float heightTranslation = dpf2(-1) * keyboardT + height - heightAnimated; if (Math.abs(lastHeightTranslation - heightTranslation) >= 1) { editText.getEditText().setTranslationY(heightTranslation); } lastHeightTranslation = heightTranslation; final float pad = lerp(AndroidUtilities.dp(12), 0, keyboardT); - AndroidUtilities.rectTmp.set( + bounds.set( pad, getHeight() - pad - heightAnimated, getWidth() - pad, @@ -672,12 +761,69 @@ protected void dispatchDraw(Canvas canvas) { ); final float r = lerp(AndroidUtilities.dp(21), 0, keyboardT); - drawBackground(canvas, AndroidUtilities.rectTmp, r, 1f, this); + if (customBlur()) { + drawBlur(backgroundBlur, canvas, bounds, r, false, 0, 0, true); + backgroundPaint.setAlpha((int) (lerp(0x26, 0x40, keyboardT))); + canvas.drawRoundRect(bounds, r, r, backgroundPaint); + } else { + Paint[] blurPaints = backgroundBlur.getPaints(1f, 0, 0); + if (blurPaints == null || blurPaints[1] == null) { + backgroundPaint.setAlpha(0x80); + canvas.drawRoundRect(bounds, r, r, backgroundPaint); + } else { + if (blurPaints[0] != null) { + canvas.drawRoundRect(bounds, r, r, blurPaints[0]); + } + if (blurPaints[1] != null) { + canvas.drawRoundRect(bounds, r, r, blurPaints[1]); + } + backgroundPaint.setAlpha(0x33); + canvas.drawRoundRect(bounds, r, r, backgroundPaint); + } + } - canvas.save(); - canvas.clipRect(AndroidUtilities.rectTmp); super.dispatchDraw(canvas); - canvas.restore(); + } + + public RectF getBounds() { + return bounds; + } + + private void drawHint(Canvas canvas, Runnable draw) { + if (customBlur()) { + if (hintTextBitmap == null) { + draw.run(); + return; + } + final EditTextCaption e = editText.getEditText(); + canvas.saveLayerAlpha(0, 0, hintTextBitmap.getWidth(), hintTextBitmap.getHeight(), 0xff, Canvas.ALL_SAVE_FLAG); + rectF.set(0, 1, hintTextBitmap.getWidth(), hintTextBitmap.getHeight() - 1); + drawBlur(captionBlur, canvas, rectF, 0, true, -editText.getX() - e.getPaddingLeft(), -editText.getY() - e.getPaddingTop() - e.getExtendedPaddingTop(), true); + canvas.save(); + hintTextBitmapPaint.setAlpha(0xa5); + canvas.drawBitmap(hintTextBitmap, 0, 0, hintTextBitmapPaint); + canvas.restore(); + canvas.restore(); + return; + } + Paint blurPaint = captionBlur.getPaint(1f); + if (blurPaint == null) { + draw.run(); + } else { + final EditTextCaption e = editText.getEditText(); + canvas.saveLayerAlpha(0, 0, e.getWidth(), e.getHeight(), 0xff, Canvas.ALL_SAVE_FLAG); + draw.run(); + canvas.drawRect(0, 0, e.getWidth(), e.getHeight(), blurPaint); + canvas.restore(); + } + } + + protected boolean customBlur() { + return false; + } + + protected void drawBlur(BlurringShader.StoryBlurDrawer blur, Canvas canvas, RectF rect, float r, boolean text, float ox, float oy, boolean thisView) { + } private void drawBackground(Canvas canvas, RectF rectF, float r, float alpha, View view) { @@ -703,45 +849,55 @@ private void drawBackground(Canvas canvas, RectF rectF, float r, float alpha, Vi @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { if (child == editText) { - final float pad = 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); + float ty = Math.max(0, editText.getHeight() - dp(82) - editText.getScrollY()) * (1f - keyboardT); canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), 0xFF, Canvas.ALL_SAVE_FLAG); canvas.save(); + canvas.clipRect(bounds); 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); + matrix.postTranslate(0, bounds.top - 1); fadeGradient.setLocalMatrix(matrix); - canvas.drawRect(AndroidUtilities.rectTmp.left, AndroidUtilities.rectTmp.top, AndroidUtilities.rectTmp.right, AndroidUtilities.rectTmp.top + AndroidUtilities.dp(10), fadePaint); + canvas.drawRect(bounds.left, bounds.top, bounds.right, bounds.top + dp(10), fadePaint); matrix.reset(); matrix.postRotate(180); - matrix.postTranslate(0, AndroidUtilities.rectTmp.bottom); + matrix.postTranslate(0, bounds.bottom); fadeGradient.setLocalMatrix(matrix); - canvas.drawRect(AndroidUtilities.rectTmp.left, AndroidUtilities.rectTmp.bottom - AndroidUtilities.dp(10), AndroidUtilities.rectTmp.right, AndroidUtilities.rectTmp.bottom, fadePaint); + canvas.drawRect(bounds.left, bounds.bottom - dp(10), bounds.right, bounds.bottom, fadePaint); canvas.restore(); canvas.restore(); + return result; + } else if (clipChild(child)) { + canvas.save(); + canvas.clipRect(bounds); + final boolean result = super.drawChild(canvas, child, drawingTime); + canvas.restore(); return result; } return super.drawChild(canvas, child, drawingTime); } + protected boolean clipChild(View child) { + return true; + } + public void clearFocus() { editText.clearFocus(); } public void clear() { + ignoreTextChange = true; editText.setText(""); } public void setText(CharSequence text) { + ignoreTextChange = true; editText.setText(text); } @@ -787,5 +943,161 @@ public void draw(Canvas canvas) { } } + public int getSelectionLength() { + if (editText == null || editText.getEditText() == null) { + return 0; + } + try { + return editText.getEditText().getSelectionEnd() - editText.getEditText().getSelectionStart(); + } catch (Exception e) { + FileLog.e(e); + } + return 0; + } + + public void updateColors(Theme.ResourcesProvider resourcesProvider) { + this.resourcesProvider = resourcesProvider; + applyButtonDrawable.setBackgroundDrawable(Theme.createCircleDrawable(AndroidUtilities.dp(16), Theme.getColor(Theme.key_dialogFloatingButton, resourcesProvider))); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (customBlur()) { + if (hintTextBitmap != null) { + hintTextBitmap.recycle(); + hintTextBitmap = null; + } + hintTextPaint.setColor(0xff000000); + hintTextPaint.setTextSize(dp(16)); + final String text = LocaleController.getString(R.string.AddCaption); + final int w = (int) Math.ceil(hintTextPaint.measureText(text)); + final int h = (int) Math.ceil(hintTextPaint.getFontMetrics().descent - hintTextPaint.getFontMetrics().ascent); + hintTextBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(hintTextBitmap); + canvas.drawText(text, 0, -(int) hintTextPaint.getFontMetrics().ascent, hintTextPaint); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (blurBitmap != null) { + blurBitmap.recycle(); + } + blurBitmapShader = null; + blurPaint = null; + if (hintTextBitmap != null) { + hintTextBitmap.recycle(); + hintTextBitmap = null; + } + } + + public static class PeriodDrawable extends Drawable { + + private final Paint strokePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint fillPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + public final AnimatedTextView.AnimatedTextDrawable textDrawable = new AnimatedTextView.AnimatedTextDrawable(true, false, false) { + @Override + public void invalidateSelf() { + PeriodDrawable.this.invalidateSelf(); + } + }; + + private boolean filled = false; + private final AnimatedFloat fillT = new AnimatedFloat(this::invalidateSelf, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + + public PeriodDrawable() { + strokePaint.setStyle(Paint.Style.STROKE); + strokePaint.setStrokeWidth(dpf2(1.66f)); + strokePaint.setStrokeCap(Paint.Cap.ROUND); + + textDrawable.setAnimationProperties(.3f, 0, 250, CubicBezierInterpolator.EASE_OUT_QUINT); + textDrawable.setTypeface(AndroidUtilities.getTypeface("fonts/num.otf")); + textDrawable.setTextSize(dpf2(12)); + textDrawable.setGravity(Gravity.CENTER); + + updateColors(0xffffffff, 0xff1A9CFF); + } + + public void updateColors(int strokeColor, int fillColor) { + strokePaint.setColor(strokeColor); + textDrawable.setTextColor(strokeColor); + fillPaint.setColor(fillColor); + } + + @Override + public void draw(@NonNull Canvas canvas) { + final float cx = getBounds().centerY(); + final float cy = getBounds().centerY(); + + final float r = dpf2(21) / 2f; + final float fillT = this.fillT.set(filled); + + if (fillT > 0) { + fillPaint.setAlpha((int) (0xFF * fillT)); + canvas.drawCircle(cx, cy, dpf2(11.33f) * fillT, fillPaint); + } + + strokePaint.setAlpha((int) (0xFF * (1f - fillT))); + AndroidUtilities.rectTmp.set(cx - r, cy - r, cx + r, cy + r); + canvas.drawArc(AndroidUtilities.rectTmp, 90, 180, false, strokePaint); + + final int dashes = 5; + final int gaps = dashes + 1; + final float dashWeight = 1f, gapWeight = 1.5f; + final float dashSweep = dashWeight / (dashes * dashWeight + gaps * gapWeight) * 180; + final float gapSweep = gapWeight / (dashes * dashWeight + gaps * gapWeight) * 180; + float a = gapSweep; + for (int i = 0; i < dashes; ++i) { + canvas.drawArc(AndroidUtilities.rectTmp, 270 + a, dashSweep, false, strokePaint); + a += dashSweep + gapSweep; + } + + canvas.save(); + canvas.translate(0, -1); + AndroidUtilities.rectTmp2.set( + (int) (cx - dp(20)), + (int) (cy - dp(20)), + (int) (cx + dp(20)), + (int) (cy + dp(20)) + ); + textDrawable.setBounds(AndroidUtilities.rectTmp2); + textDrawable.draw(canvas); + canvas.restore(); + } + + public void setValue(int num, boolean fill, boolean animated) { + textDrawable.setText("" + num, animated); + filled = fill; + if (!animated) { + fillT.set(filled, true); + } + invalidateSelf(); + } + + @Override + public void setAlpha(int alpha) { + + } + + @Override + public int getIntrinsicHeight() { + return dp(24); + } + + @Override + public int getIntrinsicWidth() { + return dp(24); + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) {} + + @Override + public int getOpacity() { + return PixelFormat.TRANSPARENT; + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/CaptionStory.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/CaptionStory.java new file mode 100644 index 0000000000..98ba6fbdab --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/CaptionStory.java @@ -0,0 +1,159 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.ui.ActionBar.Theme.RIPPLE_MASK_CIRCLE_20DP; + +import android.content.Context; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; +import android.view.Gravity; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; + +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.Utilities; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.BlurringShader; +import org.telegram.ui.Components.ItemOptions; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.SizeNotifierFrameLayout; + +public class CaptionStory extends CaptionContainerView { + + public ImageView periodButton; + public PeriodDrawable periodDrawable; + 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; + + public CaptionStory(Context context, FrameLayout rootView, SizeNotifierFrameLayout sizeNotifierFrameLayout, FrameLayout containerView, Theme.ResourcesProvider resourcesProvider, BlurringShader.BlurManager blurManager) { + super(context, rootView, sizeNotifierFrameLayout, containerView, resourcesProvider, blurManager); + + periodButton = new ImageView(context); + periodButton.setImageDrawable(periodDrawable = new PeriodDrawable()); + 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); + periodPopup.addText(LocaleController.getString("StoryPeriodHint"), 13); + periodPopup.addGap(); + 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), + Theme.key_actionBarDefaultSubmenuItem, + () -> onPeriodSelected.run(period) + ).putPremiumLock( + isPremium || period == 86400 || period == Integer.MAX_VALUE ? null : () -> showPremiumHint.run(period) + ); + if (periodIndex == i) { + periodPopup.putCheck(); + } + } + periodPopup.setDimAlpha(0).show(); + }); + setPeriod(86400, false); + addView(periodButton, LayoutHelper.createFrame(44, 44, Gravity.RIGHT | Gravity.BOTTOM, 0, 0, 11, 10)); + } + + 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; + } + periodIndex = index; + periodDrawable.setValue(period / 3600, false, animated); + } + + public void hidePeriodPopup() { + if (periodPopup != null) { + periodPopup.dismiss(); + periodPopup = null; + } + } + + 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; + } + + @Override + protected void beforeUpdateShownKeyboard(boolean show) { + if (!show) { + periodButton.setVisibility(periodVisible ? View.VISIBLE : View.GONE); + } + } + + @Override + protected void onUpdateShowKeyboard(float keyboardT) { + periodButton.setAlpha(1f - keyboardT); + } + + @Override + protected void afterUpdateShownKeyboard(boolean show) { + periodButton.setVisibility(!show && periodVisible ? View.VISIBLE : View.GONE); + if (show) { + periodButton.setVisibility(View.GONE); + } + } + + @Override + protected int getCaptionPremiumLimit() { + return MessagesController.getInstance(currentAccount).storyCaptionLengthLimitPremium; + } + + @Override + protected int getCaptionDefaultLimit() { + return MessagesController.getInstance(currentAccount).storyCaptionLengthLimitDefault; + } +} 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 index 765a54615a..15fb236dc0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DownloadButton.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DownloadButton.java @@ -145,6 +145,17 @@ private void onClick() { preparing = true; prepare.run(this::onClickInternal); } + updateImage(); + if (prepare == null) { + onClickInternal(); + } + } + + private void onClickInternal() { + if (!preparing || currentEntry == null) { + return; + } + preparing = false; if (currentEntry.wouldBeVideo()) { downloadingVideo = true; toast = new PreparingVideoToast(getContext()); @@ -161,24 +172,7 @@ private void onClick() { updateImage(); }); container.addView(toast); - } else { - downloadingVideo = false; - } - updateImage(); - if (prepare != null) { - preparing = true; - prepare.run(this::onClickInternal); - } else { - onClickInternal(); - } - } - private void onClickInternal() { - if (!preparing || currentEntry == null) { - return; - } - preparing = false; - if (currentEntry.wouldBeVideo()) { final File file = AndroidUtilities.generateVideoPath(); buildingVideo = new BuildingVideo(currentAccount, currentEntry, file, () -> { if (!downloading || currentEntry == null) { @@ -207,6 +201,7 @@ private void onClickInternal() { updateImage(); }); } else { + downloadingVideo = false; final File file = AndroidUtilities.generatePicturePath(false, "png"); if (file == null) { toast.setDone(R.raw.error, LocaleController.getString("UnknownError"), 3500); 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 index 9a58c1a72e..15b10cb9b0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DraftsController.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DraftsController.java @@ -488,6 +488,13 @@ public static class StoryDraft { public boolean isError; public TLRPC.TL_error error; + public String audioPath; + public String audioAuthor, audioTitle; + public long audioDuration; + public long audioOffset; + public float audioLeft, audioRight = 1; + public float audioVolume = 1; + public StoryDraft(@NonNull StoryEntry entry) { this.id = entry.draftId; this.date = entry.draftDate; @@ -525,6 +532,15 @@ public StoryDraft(@NonNull StoryEntry entry) { this.parts.addAll(entry.parts); this.isError = entry.isError; this.error = entry.error; + + this.audioPath = entry.audioPath; + this.audioAuthor = entry.audioAuthor; + this.audioTitle = entry.audioTitle; + this.audioDuration = entry.audioDuration; + this.audioOffset = entry.audioOffset; + this.audioLeft = entry.audioLeft; + this.audioRight = entry.audioRight; + this.audioVolume = entry.audioVolume; } public StoryEntry toEntry() { @@ -599,6 +615,15 @@ public StoryEntry toEntry() { entry.editDocumentId = editDocumentId; entry.isError = isError; entry.error = error; + + entry.audioPath = audioPath; + entry.audioAuthor = audioAuthor; + entry.audioTitle = audioTitle; + entry.audioDuration = audioDuration; + entry.audioOffset = audioOffset; + entry.audioLeft = audioLeft; + entry.audioRight = audioRight; + entry.audioVolume = audioVolume; return entry; } @@ -683,6 +708,30 @@ public void toStream(AbstractSerializedData stream) { error.serializeToStream(stream); } stream.writeString(fullThumb); + + if (audioPath == null) { + stream.writeInt32(TLRPC.TL_null.constructor); + } else { + stream.writeInt32(TLRPC.TL_documentAttributeAudio.constructor); + stream.writeString(audioPath); + if (audioAuthor == null) { + stream.writeInt32(TLRPC.TL_null.constructor); + } else { + stream.writeInt32(TLRPC.TL_jsonString.constructor); + stream.writeString(audioAuthor); + } + if (audioTitle == null) { + stream.writeInt32(TLRPC.TL_null.constructor); + } else { + stream.writeInt32(TLRPC.TL_jsonString.constructor); + stream.writeString(audioTitle); + } + stream.writeInt64(audioDuration); + stream.writeInt64(audioOffset); + stream.writeFloat(audioLeft); + stream.writeFloat(audioRight); + stream.writeFloat(audioVolume); + } } public int getObjectSize() { @@ -838,6 +887,25 @@ public StoryDraft(@NonNull AbstractSerializedData stream, boolean exception) { } fullThumb = stream.readString(exception); } + if (stream.remaining() > 0) { + magic = stream.readInt32(exception); + if (magic == TLRPC.TL_documentAttributeAudio.constructor) { + audioPath = stream.readString(exception); + magic = stream.readInt32(exception); + if (magic == TLRPC.TL_jsonString.constructor) { + audioAuthor = stream.readString(exception); + } + magic = stream.readInt32(exception); + if (magic == TLRPC.TL_jsonString.constructor) { + audioTitle = stream.readString(exception); + } + audioDuration = stream.readInt64(exception); + audioOffset = stream.readInt64(exception); + audioLeft = stream.readFloat(exception); + audioRight = stream.readFloat(exception); + audioVolume = stream.readFloat(exception); + } + } } } } 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 index 991b78695e..848269d9e9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DualCameraView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DualCameraView.java @@ -542,7 +542,7 @@ public boolean dualAvailable() { 846150482, // MOTOROLA CHANNEL -1198092731, // MOTOROLA CYPRUS64 -251277614, // MOTOROLA HANOIP - -2078385967, // MOTOROLA PSTAR +// -2078385967, // MOTOROLA PSTAR -2073158771, // MOTOROLA VICKY 1273004781 // MOTOROLA BLACKJACK // -1426053134 // REALME REE2ADL1 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 index 0ea721d7ab..b21f8f0bc6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/EmojiBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/EmojiBottomSheet.java @@ -1,9 +1,12 @@ 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 static org.telegram.messenger.AndroidUtilities.translitSafe; +import android.app.Activity; +import android.app.Dialog; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapShader; @@ -30,6 +33,7 @@ import android.util.SparseIntArray; import android.util.TypedValue; import android.view.Gravity; +import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @@ -41,6 +45,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; import androidx.core.graphics.ColorUtils; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.GridLayoutManager; @@ -71,6 +76,7 @@ import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.AdjustPanLayoutHelper; +import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Cells.ContextLinkCell; @@ -79,6 +85,8 @@ import org.telegram.ui.Components.AnimatedFileDrawable; import org.telegram.ui.Components.AnimatedFloat; import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.Bulletin; +import org.telegram.ui.Components.BulletinFactory; import org.telegram.ui.Components.ButtonBounce; import org.telegram.ui.Components.CloseProgressDrawable2; import org.telegram.ui.Components.CubicBezierInterpolator; @@ -88,16 +96,22 @@ import org.telegram.ui.Components.EmojiView; import org.telegram.ui.Components.ExtendedGridLayoutManager; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.Premium.PremiumFeatureBottomSheet; import org.telegram.ui.Components.RLottieDrawable; +import org.telegram.ui.Components.Reactions.ReactionImageHolder; +import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; 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.Size; import org.telegram.ui.Components.StickerCategoriesListView; import org.telegram.ui.Components.ViewPagerFixed; import org.telegram.ui.ContentPreviewViewer; +import org.telegram.ui.LaunchActivity; +import org.telegram.ui.PremiumPreviewFragment; import org.telegram.ui.SelectAnimatedEmojiDialog; +import org.telegram.ui.Stories.StoryReactionWidgetBackground; +import org.telegram.ui.WrappedResourceProvider; import java.util.ArrayList; import java.util.Arrays; @@ -105,6 +119,8 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; public class EmojiBottomSheet extends BottomSheet implements NotificationCenter.NotificationCenterDelegate { @@ -115,7 +131,7 @@ public class EmojiBottomSheet extends BottomSheet implements NotificationCenter. private String query = null; private int categoryIndex = -1; - public final TLRPC.Document locationSticker = new TLRPC.Document() {}; + public final TLRPC.Document widgets = new TLRPC.Document() {}; abstract class IPage extends FrameLayout { public int currentType; @@ -541,7 +557,7 @@ public Page(Context context) { layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { - if (adapter.getItemViewType(position) != Adapter.VIEW_TYPE_EMOJI) { + if (adapter.getItemViewType(position) != VIEW_TYPE_EMOJI) { return spanCount; } return 1; @@ -551,6 +567,9 @@ public int getSpanSize(int position) { if (position < 0) { return; } + if (layoutManager.getItemViewType(view) == VIEW_TYPE_WIDGETS) { + 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) { @@ -743,6 +762,12 @@ public boolean atTop() { return !listView.canScrollVertically(-1); } + 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; + private static final int VIEW_TYPE_WIDGETS = 4; + private class Adapter extends RecyclerView.Adapter { private int lastAllSetsCount; @@ -758,6 +783,13 @@ private class Adapter extends RecyclerView.Adapter { private int itemsCount = 0; private final SparseIntArray positionToSection = new SparseIntArray(); + private final TLRPC.TL_inputStickerSetShortName staticEmojiInput; + + public Adapter() { + staticEmojiInput = new TLRPC.TL_inputStickerSetShortName(); + staticEmojiInput.short_name = "StaticEmoji"; + } + public void update() { if (this.query == null) { updateItems(null); @@ -793,7 +825,7 @@ private void updateItems(String query) { packs.clear(); int i = 0; if (currentType == PAGE_TYPE_STICKERS) { - documents.add(locationSticker); + documents.add(widgets); itemsCount++; ArrayList favorites = mediaDataController.getRecentStickers(MediaDataController.TYPE_FAVE); @@ -948,9 +980,7 @@ private void updateItems(String query) { } } if (!containsStaticEmoji) { - TLRPC.TL_inputStickerSetShortName inputStickerSet = new TLRPC.TL_inputStickerSetShortName(); - inputStickerSet.short_name = "StaticEmoji"; - TLRPC.TL_messages_stickerSet set = mediaDataController.getStickerSet(inputStickerSet, false); + TLRPC.TL_messages_stickerSet set = mediaDataController.getStickerSet(staticEmojiInput, false); if (set != null) { allStickerSets.add(set); } @@ -1089,12 +1119,6 @@ private void updateItems(String query) { }, 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; - private static final int VIEW_TYPE_COMPONENT = 4; - @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { @@ -1105,8 +1129,15 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int view = new StickerSetNameCell(getContext(), true, resourcesProvider); } else if (viewType == VIEW_TYPE_NOT_FOUND) { view = new NoEmojiView(getContext(), currentType == PAGE_TYPE_EMOJI); - } else if (viewType == VIEW_TYPE_COMPONENT) { - view = new StoryLocationComponentCell(getContext()); + } else if (viewType == VIEW_TYPE_WIDGETS) { + StoryWidgetsCell cell = new StoryWidgetsCell(getContext()); + cell.setOnButtonClickListener(id -> { + if (canShowWidget(id)) { + onWidgetSelected.run(id); + dismiss(); + } + }); + view = cell; } else { view = new EmojiListView.EmojiImageView(getContext(), listView); } @@ -1170,8 +1201,8 @@ public int getItemViewType(int position) { } else if (positionToSection.get(position, -1) >= 0) { return VIEW_TYPE_HEADER; } else { - if (position >= 0 && position < documents.size() && documents.get(position) == locationSticker) { - return VIEW_TYPE_COMPONENT; + if (position >= 0 && position < documents.size() && documents.get(position) == widgets) { + return VIEW_TYPE_WIDGETS; } return VIEW_TYPE_EMOJI; } @@ -1184,6 +1215,59 @@ public int getItemCount() { } } + public void showPremiumBulletin(String str, int resId) { + container.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + BulletinFactory.of(container, resourcesProvider).createSimpleBulletin( + ContextCompat.getDrawable(getContext(), R.drawable.msg_premium_normal), + LocaleController.getString("IncreaseLimit", R.string.IncreaseLimit), + premiumText(LocaleController.getString(str, resId)) + ).show(true); + } + + private CharSequence premiumText(String text) { + return AndroidUtilities.replaceSingleTag(text, Theme.key_chat_messageLinkIn, 0, this::openPremium, resourcesProvider); + } + + private void openPremium() { + Bulletin.hideVisible(); + PremiumFeatureBottomSheet sheet = new PremiumFeatureBottomSheet(new BaseFragment() { + { currentAccount = EmojiBottomSheet.this.currentAccount; } + @Override + public Dialog showDialog(Dialog dialog) { + dialog.show(); + return dialog; + } + @Override + public Activity getParentActivity() { + return LaunchActivity.instance; + } + + @Override + public Theme.ResourcesProvider getResourceProvider() { + return new WrappedResourceProvider(resourcesProvider) { + @Override + public void appendColors() { + sparseIntArray.append(Theme.key_dialogBackground, 0xFF1E1E1E); + sparseIntArray.append(Theme.key_windowBackgroundGray, 0xFF000000); + } + }; + } + + @Override + public boolean isLightStatusBar() { + return false; + } + }, PremiumPreviewFragment.PREMIUM_FEATURE_STORIES, false); + sheet.setOnDismissListener(d -> { + + }); + sheet.show(); + } + + public boolean canShowWidget(Integer id) { + return true; + } + @Override public void didReceivedNotification(int id, int account, Object... args) { if (id == NotificationCenter.stickersDidLoad || id == NotificationCenter.groupStickersDidLoad) { @@ -1205,17 +1289,18 @@ public void didReceivedNotification(int id, int account, Object... args) { private final ViewPagerFixed viewPager; private final TabsView tabsView; - private final ImageView galleryButton; private float maxPadding = -1; // private final GestureDetector gestureDetector; private boolean wasKeyboardVisible; public static int savedPosition = 1; + private boolean storyIsVideo; - public EmojiBottomSheet(Context context, Theme.ResourcesProvider resourcesProvider) { + public EmojiBottomSheet(Context context, boolean storyIsVideo, Theme.ResourcesProvider resourcesProvider) { super(context, true, resourcesProvider); + this.storyIsVideo = storyIsVideo; useSmoothKeyboard = true; fixNavigationBar(Theme.getColor(Theme.key_dialogBackground, resourcesProvider)); @@ -1225,7 +1310,7 @@ public EmojiBottomSheet(Context context, Theme.ResourcesProvider resourcesProvid containerView = new ContainerView(context); viewPager = new ViewPagerFixed(context) { @Override - protected void onTabAnimationUpdate() { + protected void onTabAnimationUpdate(boolean manual) { tabsView.setType(viewPager.getPositionAnimated()); containerView.invalidate(); invalidate(); @@ -1281,14 +1366,6 @@ public void bindView(View view, int position, int viewType) { tabsView.setType(viewPager.currentPosition); 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); @@ -1320,11 +1397,6 @@ public void closeKeyboard() { } } - 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); @@ -1412,8 +1484,6 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 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); } @@ -1479,10 +1549,15 @@ public int getContainerViewHeight() { } private Utilities.Callback3 onDocumentSelected; - public EmojiBottomSheet whenSelected(Utilities.Callback3 listener) { + public EmojiBottomSheet whenDocumentSelected(Utilities.Callback3 listener) { this.onDocumentSelected = listener; return this; } + private Utilities.Callback onWidgetSelected; + public EmojiBottomSheet whenWidgetSelected(Utilities.Callback listener) { + this.onWidgetSelected = listener; + return this; + } @Override protected boolean canDismissWithSwipe() { @@ -2482,81 +2557,176 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } } - private static class StoryLocationComponentCell extends View { + public static final int WIDGET_LOCATION = 0; + public static final int WIDGET_PHOTO = 2; - private final TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); - private final Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private class StoryWidgetsCell extends View { - private final Drawable pin; - private StaticLayout layout; - private float layoutLeft, layoutWidth; + private final Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + { + bgPaint.setColor(0x19ffffff); + textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rcondensedbold.ttf")); + textPaint.setTextSize(dpf2(21.3f)); + textPaint.setColor(Color.WHITE); + } - private final RectF bounds = new RectF(); - private final ButtonBounce bounce = new ButtonBounce(this); + private final List widgets = new ArrayList<>(); - public StoryLocationComponentCell(Context context) { + public StoryWidgetsCell(Context context) { super(context); + setPadding(dp(0), 0, dp(0), 0); - textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rcondensedbold.ttf")); - textPaint.setTextSize(dp(21.3f)); - textPaint.setColor(Color.WHITE); + widgets.add(new Button(WIDGET_LOCATION, R.drawable.map_pin3, LocaleController.getString(R.string.StoryWidgetLocation))); + widgets.add(new Button(WIDGET_PHOTO, R.drawable.files_gallery, LocaleController.getString(R.string.StoryWidgetPhoto))); + } - pin = context.getResources().getDrawable(R.drawable.map_pin3).mutate(); - pin.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); + private abstract class BaseWidget { + int id; + float width, height; + float layoutX = 0; + int layoutLine = 0; + RectF bounds = new RectF(); + ButtonBounce bounce = new ButtonBounce(StoryWidgetsCell.this); - bgPaint.setColor(0x19ffffff); - } + abstract void draw(Canvas canvas, float left, float top); - private int lastWidth; + public void onAttachToWindow(boolean attached) { - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(dp(60), MeasureSpec.EXACTLY)); - - if (lastWidth != getMeasuredWidth()) { - CharSequence text = LocaleController.getString("AddLocation", R.string.AddLocation); - text = TextUtils.ellipsize(text, textPaint, getMeasuredWidth(), TextUtils.TruncateAt.END); - layout = new StaticLayout(text, textPaint, getMeasuredWidth(), Layout.Alignment.ALIGN_NORMAL, 1f, 0, false); - layoutLeft = layout.getLineCount() <= 0 ? 0 : layout.getLineLeft(0); - layoutWidth = layout.getLineCount() <= 0 ? 0 : layout.getLineWidth(0); - lastWidth = getMeasuredWidth(); - - float width = dp(6 + 24 + 4 + 11.6f) + layoutWidth; - float height = dp(6 + 5) + layout.getHeight(); - bounds.set( - (getMeasuredWidth() - width) / 2f, - (getMeasuredHeight() - height) / 2f, - (getMeasuredWidth() + width) / 2f, - (getMeasuredHeight() + height) / 2f - ); + } + + } - pin.setBounds( + private class Button extends BaseWidget { + Drawable drawable; + StaticLayout layout; + float textWidth; + float textLeft; + + public Button(int id, int iconId, String string) { + this.id = id; + this.drawable = getContext().getResources().getDrawable(iconId).mutate(); + this.drawable.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); + CharSequence text = string.toUpperCase(); + text = TextUtils.ellipsize(text, textPaint, AndroidUtilities.displaySize.x * .8f, TextUtils.TruncateAt.END); + this.layout = new StaticLayout(text, textPaint, 99999, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false); + this.textWidth = this.layout.getLineCount() > 0 ? this.layout.getLineWidth(0) : 0; + this.textLeft = this.layout.getLineCount() > 0 ? this.layout.getLineLeft(0) : 0; + this.width = dpf2(6 + 24 + 4 + 11.6f) + this.textWidth; + this.height = dpf2(36); + } + + public void draw(Canvas canvas, float left, float top) { + bounds.set(left, top, left + width, top + height); + final float scale = bounce.getScale(.05f); + canvas.save(); + canvas.scale(scale, scale, bounds.centerX(), bounds.centerY()); + canvas.drawRoundRect(bounds, dp(8), dp(8), bgPaint); + drawable.setBounds( (int) (bounds.left + dp(6)), - (int) (bounds.centerY() - dp(12)), + (int) (bounds.top + height / 2 - dp(24) / 2), (int) (bounds.left + dp(6 + 24)), - (int) (bounds.centerY() + dp(12)) + (int) (bounds.top + height / 2 + dp(24) / 2) ); + drawable.draw(canvas); + canvas.translate(bounds.left + dp(6 + 24 + 4) - textLeft, bounds.top + height / 2 - layout.getHeight() / 2f); + layout.draw(canvas); + canvas.restore(); + } + } + + float[] lineWidths; + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int y = 1; + float x = 0; + + final int width = MeasureSpec.getSize(widthMeasureSpec); + final int availableWidth = (int) ((width - getPaddingLeft() - getPaddingRight()) * 0.8f); + + for (final BaseWidget widget : widgets) { + widget.layoutX = x; + x += widget.width + dp(10); + if (x > availableWidth) { + y++; + widget.layoutX = x = 0; + x += widget.width + dp(10); + } + widget.layoutLine = y; } + + final int linesCount = y; + if (lineWidths == null || lineWidths.length != linesCount) { + lineWidths = new float[linesCount]; + } else { + Arrays.fill(lineWidths, 0); + } + for (final BaseWidget widget : widgets) { + final int i = widget.layoutLine - 1; + if (lineWidths[i] > 0) + lineWidths[i] += dp(10); + lineWidths[i] += widget.width; + } + + final int height = dp(12 + 12) + y * dp(36) + (y - 1) * dp(12); + setMeasuredDimension(width, height); } @Override protected void dispatchDraw(Canvas canvas) { - canvas.save(); - final float scale = bounce.getScale(.1f); - canvas.scale(scale, scale, bounds.centerX(), bounds.centerY()); - canvas.drawRoundRect(bounds, dp(8), dp(8), bgPaint); - pin.draw(canvas); - canvas.save(); - canvas.translate(bounds.left + dp(6 + 24 + 4) - layoutLeft, bounds.top + dp(6)); - layout.draw(canvas); - canvas.restore(); - canvas.restore(); + for (final BaseWidget widget : widgets) { + final float left = getPaddingLeft() + ((getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - lineWidths[widget.layoutLine - 1]) / 2f) + widget.layoutX; + final float top = dp(12) + (widget.layoutLine - 1) * dp(36 + 12); + widget.draw(canvas, left, top); + } } @Override - public void setPressed(boolean pressed) { - super.setPressed(pressed); - bounce.setPressed(pressed); + public boolean onTouchEvent(MotionEvent event) { + BaseWidget touchButton = null; + for (final BaseWidget widget : widgets) { + if (widget.bounds.contains(event.getX(), event.getY())) { + touchButton = widget; + break; + } + } + for (final BaseWidget widget : widgets) { + if (widget != touchButton) { + widget.bounce.setPressed(false); + } + } + if (touchButton != null) { + touchButton.bounce.setPressed(event.getAction() != MotionEvent.ACTION_UP && event.getAction() != MotionEvent.ACTION_CANCEL); + } + if (event.getAction() == MotionEvent.ACTION_UP && touchButton != null) { + if (onClickListener != null) { + onClickListener.run(touchButton.id); + } + } + return touchButton != null; + } + + private Utilities.Callback onClickListener; + public void setOnButtonClickListener(Utilities.Callback listener) { + onClickListener = listener; + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + for (BaseWidget widget : widgets) { + widget.onAttachToWindow(true); + } + } + + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + for (BaseWidget widget : widgets) { + widget.onAttachToWindow(false); + } } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/FfmpegAudioWaveformLoader.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/FfmpegAudioWaveformLoader.java new file mode 100644 index 0000000000..767a07a937 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/FfmpegAudioWaveformLoader.java @@ -0,0 +1,31 @@ +package org.telegram.ui.Stories.recorder; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.Utilities; + +public class FfmpegAudioWaveformLoader { + + private volatile boolean running = true; + + private native void init(String path, int count); + + public FfmpegAudioWaveformLoader(String path, int count, Utilities.Callback2 onChunkReceived) { + this.onChunkReceived = onChunkReceived; + Utilities.phoneBookQueue.postRunnable(() -> { + init(path, count); + }); + } + + private Utilities.Callback2 onChunkReceived; + private void receiveChunk(short[] data, int len) { + AndroidUtilities.runOnUIThread(() -> { + onChunkReceived.run(data, len); + }); + } + + public void destroy() { + Utilities.phoneBookQueue.postRunnable(() -> { + running = false; + }); + } +} 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 index 4ac5d3fe09..3b7e392629 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/HintView2.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/HintView2.java @@ -28,6 +28,7 @@ import android.text.Layout; import android.text.Spannable; import android.text.SpannableString; +import android.text.Spanned; import android.text.StaticLayout; import android.text.TextPaint; import android.text.style.ClickableSpan; @@ -53,6 +54,8 @@ import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.LinkPath; import org.telegram.ui.Components.LinkSpanDrawable; +import org.telegram.ui.Components.RLottieDrawable; +import org.telegram.ui.Components.TypefaceSpan; public class HintView2 extends View { @@ -92,7 +95,7 @@ public class HintView2 extends View { 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 float textLayoutLeft, textLayoutWidth, textLayoutHeight; private LinkSpanDrawable.LinkCollector links = new LinkSpanDrawable.LinkCollector(); private float textX, textY; @@ -105,6 +108,12 @@ public class HintView2 extends View { private Drawable selectorDrawable; private Paint cutSelectorPaint; + private Drawable icon; + private float iconTx, iconTy; + private int iconMargin = dp(2); + private int iconWidth, iconHeight; + private boolean iconLeft; + public HintView2(Context context, int direction) { super(context); this.direction = direction; @@ -203,6 +212,62 @@ public HintView2 setMaxWidthPx(int widthPx) { return this; } + public HintView2 setIcon(Drawable icon) { + if (this.icon != null) { + this.icon.setCallback(null); + } + this.icon = icon; + if (this.icon != null) { + this.icon.setCallback(this); + if (this.icon instanceof RLottieDrawable) { + duration = Math.max(duration, ((RLottieDrawable) this.icon).getDuration()); + } + // TODO: to be custom + this.iconWidth = this.icon.getIntrinsicWidth(); + this.iconHeight = this.icon.getIntrinsicHeight(); + this.iconLeft = true; + } + return this; + } + + private static float measureCorrectly(CharSequence text, TextPaint paint) { + if (text == null) { + return 0; + } + if (!(text instanceof Spanned)) { + return paint.measureText(text.toString()); + } + Spanned spanned = (Spanned) text; + TypefaceSpan[] spans = spanned.getSpans(0, text.length(), TypefaceSpan.class); + if (spans == null || spans.length == 0) { + return paint.measureText(text.toString()); + } + float len = 0; + int s = 0, e; + for (int i = 0; i < spans.length; ++i) { + int spanstart = spanned.getSpanStart(spans[i]); + int spanend = spanned.getSpanEnd(spans[i]); + + e = Math.max(s, spanstart); + if (e - s > 0) { + len += paint.measureText(spanned, s, e); + } + s = e; + e = Math.max(s, spanend); + if (e - s > 0) { + Typeface oldTypeface = paint.getTypeface(); + paint.setTypeface(spans[i].getTypeface()); + len += paint.measureText(spanned, s, e); + paint.setTypeface(oldTypeface); + } + } + e = Math.max(s, text.length()); + if (e - s > 0) { + len += paint.measureText(spanned, s, e); + } + return len; + } + // returns max width public static int cutInFancyHalf(CharSequence text, TextPaint paint) { int mid = text.length() / 2; @@ -216,8 +281,9 @@ public static int cutInFancyHalf(CharSequence text, TextPaint paint) { mid--; } - leftWidth = paint.measureText(text.subSequence(0, mid).toString()); - rightWidth = paint.measureText(text.subSequence(mid, text.length()).toString().trim()); + + leftWidth = measureCorrectly(text.subSequence(0, mid).toString(), paint); + rightWidth = measureCorrectly(text.subSequence(mid, text.length()).toString().trim(), paint); // If we're not making progress, exit the loop. // (This is a basic way to ensure termination when we can't improve the result.) @@ -276,6 +342,17 @@ public HintView2 setInnerPadding(int leftDp, int topDp, int rightDp, int bottomD return this; } + public HintView2 setIconMargin(int marginDp) { + this.iconMargin = dp(marginDp); + return this; + } + + public HintView2 setIconTranslate(float iconTx, float iconTy) { + this.iconTx = iconTx; + this.iconTy = iconTy; + return this; + } + public HintView2 setCloseButtonMargin(int marginDp) { this.closeButtonMargin = dp(marginDp); return this; @@ -499,6 +576,7 @@ private void makeLayout(CharSequence text, int width) { right = Math.max(right, textLayout.getLineRight(i)); } textLayoutWidth = Math.max(0, right - left); + textLayoutHeight = textLayout.getHeight(); textLayoutLeft = left; } @@ -529,7 +607,7 @@ protected void dispatchDraw(Canvas canvas) { } float contentWidth = multiline ? textLayoutWidth : textDrawable.getCurrentWidth(); - float contentHeight = multiline ? (textLayout == null ? 0 : textLayout.getHeight()) : textDrawable.getHeight(); + float contentHeight = multiline ? textLayoutHeight : textDrawable.getHeight(); if (closeButton) { if (closeButtonDrawable == null) { closeButtonDrawable = getContext().getResources().getDrawable(R.drawable.msg_mini_close_tooltip).mutate(); @@ -538,6 +616,10 @@ protected void dispatchDraw(Canvas canvas) { contentWidth += closeButtonMargin + closeButtonDrawable.getIntrinsicWidth(); contentHeight = Math.max(closeButtonDrawable.getIntrinsicHeight(), contentHeight); } + if (icon != null) { + contentWidth += iconWidth + iconMargin; + contentHeight = Math.max(iconHeight, contentHeight); + } final float width = innerPadding.left + contentWidth + innerPadding.right; final float height = innerPadding.top + contentHeight + innerPadding.bottom; @@ -567,7 +649,13 @@ protected void dispatchDraw(Canvas canvas) { } final int wasAlpha = backgroundPaint.getAlpha(); - backgroundPaint.setAlpha((int) (wasAlpha * alpha)); + AndroidUtilities.rectTmp.set(bounds); + AndroidUtilities.rectTmp.inset(-arrowHeight, -arrowHeight); + float backgroundAlpha = alpha; + if (drawBlur(canvas, AndroidUtilities.rectTmp, path, alpha)) { + backgroundAlpha *= .2f; + } + backgroundPaint.setAlpha((int) (wasAlpha * backgroundAlpha)); canvas.drawPath(path, backgroundPaint); backgroundPaint.setAlpha(wasAlpha); @@ -577,9 +665,32 @@ protected void dispatchDraw(Canvas canvas) { selectorDrawable.draw(canvas); } + final float cy = ((bounds.bottom - innerPadding.bottom) + (bounds.top + innerPadding.top)) / 2f; + float tx = 0; + if (icon != null) { + if (iconLeft) { + icon.setBounds( + (int) (iconTx + bounds.left + innerPadding.left / 2f), + (int) (iconTy + cy - iconHeight / 2f), + (int) (iconTx + bounds.left + innerPadding.left / 2f + iconWidth), + (int) (iconTy + cy + iconHeight / 2f) + ); + tx += iconWidth + iconMargin; + } else { + icon.setBounds( + (int) (iconTx + bounds.right - innerPadding.right / 2f - iconWidth), + (int) (iconTy + cy - iconHeight / 2f), + (int) (iconTx + bounds.right - innerPadding.right / 2f), + (int) (iconTy + cy + iconHeight / 2f) + ); + } + icon.setAlpha((int) (0xFF * alpha)); + icon.draw(canvas); + } + 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); + canvas.translate(textX = tx + bounds.left + innerPadding.left - textLayoutLeft, textY = cy - textLayoutHeight / 2f); if (links.draw(canvas)) { invalidate(); } @@ -590,7 +701,7 @@ protected void dispatchDraw(Canvas canvas) { 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.setBounds((int) (tx + bounds.left + innerPadding.left), (int) (cy - textLayoutHeight / 2f), (int) (bounds.left + innerPadding.left + contentWidth), (int) (cy + textLayoutHeight / 2f)); textDrawable.setAlpha((int) (0xFF * alpha)); textDrawable.draw(canvas); } @@ -612,6 +723,10 @@ protected void dispatchDraw(Canvas canvas) { canvas.restore(); } + protected boolean drawBlur(Canvas canvas, RectF bounds, Path path, float alpha) { + return false; + } + private void rewindPath(float width, float height) { float arrowXY; if (direction == DIRECTION_TOP || direction == DIRECTION_BOTTOM) { @@ -696,7 +811,7 @@ private void rewindPath(float width, float height) { @Override protected boolean verifyDrawable(@NonNull Drawable who) { - return who == textDrawable || who == selectorDrawable || super.verifyDrawable(who); + return who == textDrawable || who == selectorDrawable || who == icon || super.verifyDrawable(who); } @Override 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 index 5ac582fcad..4261cd385f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PaintView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PaintView.java @@ -1,6 +1,5 @@ package org.telegram.ui.Stories.recorder; -import static org.telegram.messenger.AndroidUtilities.accelerateInterpolator; import static org.telegram.messenger.AndroidUtilities.lerp; import android.animation.Animator; @@ -10,9 +9,6 @@ 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; @@ -25,30 +21,26 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.SweepGradient; -import android.graphics.Typeface; import android.graphics.drawable.GradientDrawable; import android.location.Address; import android.location.Geocoder; -import android.location.Location; -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.HapticFeedbackConstants; 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; @@ -67,7 +59,6 @@ //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.ApplicationLoader; import org.telegram.messenger.Bitmaps; @@ -76,13 +67,11 @@ 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; @@ -92,12 +81,13 @@ 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.Theme; import org.telegram.ui.BubbleActivity; import org.telegram.ui.ChatActivity; import org.telegram.ui.Components.AnimatedEmojiDrawable; import org.telegram.ui.Components.AnimatedEmojiSpan; +import org.telegram.ui.Components.BlurringShader; +import org.telegram.ui.Components.BulletinFactory; import org.telegram.ui.Components.ChatActivityEnterViewAnimatedIconView; import org.telegram.ui.Components.ChatAttachAlert; import org.telegram.ui.Components.CubicBezierInterpolator; @@ -132,8 +122,10 @@ 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.LaunchActivity; import org.telegram.ui.PhotoViewer; +import org.telegram.ui.Stories.DarkThemeResourceProvider; +import org.telegram.ui.WrappedResourceProvider; import java.io.File; import java.math.BigInteger; @@ -141,12 +133,13 @@ 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 { +public class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPaintView, PaintToolsView.Delegate, EntityView.EntityViewDelegate, PaintTextOptionsView.Delegate, SizeNotifierFrameLayout.SizeNotifierFrameLayoutDelegate, StoryRecorder.Touchable { private PaintCancelView cancelButton; private PaintDoneView doneButton; private float offsetTranslationY; - private Bitmap bitmapToEdit; + private final Bitmap bitmapToEdit; + private final Bitmap blurBitmapToEdit; private Bitmap facesBitmap; private UndoStore undoStore; @@ -262,9 +255,10 @@ public void set(float val) { private boolean fileFromGallery; private File file; private boolean isVideo; + private BlurringShader.BlurManager blurManager; @SuppressLint("NotifyDataSetChanged") - public PaintView(Context context, boolean fileFromGallery, File file, boolean isVideo, 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) { + public PaintView(Context context, boolean fileFromGallery, File file, boolean isVideo, StoryRecorder.WindowView parent, Activity activity, int currentAccount, Bitmap bitmap, Bitmap blurBitmap, Bitmap originalBitmap, int originalRotation, ArrayList entities, int viewWidth, int viewHeight, MediaController.CropState cropState, Runnable onInit, BlurringShader.BlurManager blurManager, Theme.ResourcesProvider resourcesProvider) { super(context, activity, true); setDelegate(this); this.fileFromGallery = fileFromGallery; @@ -355,6 +349,7 @@ public ColorFilter getAnimatedEmojiColorFilter() { queue = new DispatchQueue("Paint"); bitmapToEdit = bitmap; + blurBitmapToEdit = blurBitmap; facesBitmap = originalBitmap; originalBitmapRotation = originalRotation; undoStore = new UndoStore(); @@ -373,7 +368,7 @@ public ColorFilter getAnimatedEmojiColorFilter() { textDim.setBackgroundColor(0x4d000000); textDim.setAlpha(0f); - renderView = new RenderView(context, new Painting(getPaintingSize(), originalBitmap, originalRotation), bitmapToEdit) { + renderView = new RenderView(context, new Painting(getPaintingSize(), originalBitmap, originalRotation, blurManager), bitmapToEdit, blurBitmapToEdit, blurManager) { @Override public void selectBrush(Brush brush) { int index = 1 + Brush.BRUSHES_LIST.indexOf(brush); @@ -778,7 +773,7 @@ protected void onDraw(Canvas canvas) { 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 = new PaintToolsView(context, blurManager != 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); @@ -1006,7 +1001,7 @@ public void onColorSelected(int color) { } keyboardNotifier = new KeyboardNotifier(parent, keyboardHeight -> { - keyboardHeight = Math.max(keyboardHeight - parent.getBottomPadding(false), emojiPadding - parent.getPaddingUnderContainer()); + keyboardHeight = Math.max(keyboardHeight - parent.getBottomPadding2(), emojiPadding - parent.getPaddingUnderContainer()); keyboardHeight = Math.max(0, keyboardHeight); notifyHeightChanged(); @@ -1143,7 +1138,6 @@ private TextPaintView createText(boolean select) { 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.setStickyX(EntityView.STICKY_CENTER); } @@ -1577,7 +1571,7 @@ private void openStickersView() { detectFaces(); } }, 350); - EmojiBottomSheet alert = emojiPopup = new EmojiBottomSheet(getContext(), resourcesProvider) { + EmojiBottomSheet alert = emojiPopup = new EmojiBottomSheet(getContext(), isVideo, resourcesProvider) { @Override public void onDismissAnimationStart() { super.onDismissAnimationStart(); @@ -1585,25 +1579,30 @@ public void onDismissAnimationStart() { } }; alert.setBlurDelegate(parent::drawBlurBitmap); - alert.setOnGalleryClick(v -> { - alert.dismiss(); - onGalleryClick(); - }); + boolean[] closing = new boolean[1]; + closing[0] = true; alert.setOnDismissListener(di -> { emojiPopup = null; - onOpenCloseStickersAlert(false); + if (closing[0]) { + onOpenCloseStickersAlert(false); + } switchTab(wasSelectedIndex); }); - alert.whenSelected((parentObject, document, isGif) -> { - if (document == alert.locationSticker) { + alert.whenDocumentSelected((parentObject, document, isGif) -> { + forceChanges = true; + StickerView stickerView = createSticker(parentObject, document, false); + if (isGif) { + stickerView.setScale(1.5f); + } + appearAnimation(stickerView); + }); + alert.whenWidgetSelected(widgetId -> { + if (widgetId == EmojiBottomSheet.WIDGET_LOCATION) { + closing[0] = false; showLocationAlert(null, (location, area) -> appearAnimation(createLocationSticker(location, area, false))); - } else { - forceChanges = true; - StickerView stickerView = createSticker(parentObject, document, false); - if (isGif) { - stickerView.setScale(1.5f); - } - appearAnimation(stickerView); + } else if (widgetId == EmojiBottomSheet.WIDGET_PHOTO) { + alert.dismiss(); + onGalleryClick(); } }); alert.show(); @@ -1691,6 +1690,9 @@ public void didPressedButton(int button, boolean arg, boolean notify, int schedu } else { locationAlert.setStoryLocationPicker(); } + locationAlert.setOnDismissListener(di -> { + onOpenCloseStickersAlert(false); + }); locationAlert.init(); locationAlert.show(); } @@ -1767,10 +1769,6 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } else { hideEmojiView(); } - -// if (emojiView != null) { -// measureChild(emojiView, widthMeasureSpec, heightMeasureSpec); -// } } @Override @@ -2002,15 +2000,15 @@ public static boolean isVideoStickerDocument(TLRPC.Document document) { @Override public Bitmap getBitmap(ArrayList entities, Bitmap[] thumbBitmap) { - return getBitmap(entities, (int) paintingSize.width, (int) paintingSize.height, true, true); + return getBitmap(entities, (int) paintingSize.width, (int) paintingSize.height, true, true, false); } - public Bitmap getBitmap(ArrayList entities, int resultWidth, int resultHeight, boolean drawPaint, boolean drawEntities) { + public Bitmap getBitmap(ArrayList entities, int resultWidth, int resultHeight, boolean drawPaint, boolean drawEntities, boolean drawBlur) { Bitmap bitmap; if (drawPaint) { - bitmap = renderView.getResultBitmap(); + bitmap = renderView.getResultBitmap(false, drawBlur); } else if (drawEntities) { - Bitmap ref = renderView.getResultBitmap(); + Bitmap ref = renderView.getResultBitmap(false, false); if (ref != null) { bitmap = Bitmap.createBitmap(ref.getWidth(), ref.getHeight(), Bitmap.Config.ARGB_8888); } else { @@ -2196,8 +2194,10 @@ public Bitmap getBitmap(ArrayList entities, int res } else if (entity instanceof LocationView) { mediaEntity.mediaArea.coordinates.x = (mediaEntity.x + mediaEntity.width / 2f) * 100; mediaEntity.mediaArea.coordinates.y = (mediaEntity.y + mediaEntity.height / 2f) * 100; - mediaEntity.mediaArea.coordinates.w = (mediaEntity.width - 2 * ((LocationView) entity).marker.padx * scaleX / (float) entitiesView.getMeasuredWidth()) * 100; - mediaEntity.mediaArea.coordinates.h = (mediaEntity.height - 2 * ((LocationView) entity).marker.pady * scaleY / (float) entitiesView.getMeasuredHeight()) * 100; + if (entity instanceof LocationView) { + mediaEntity.mediaArea.coordinates.w = (mediaEntity.width - 2 * ((LocationView) entity).marker.padx * scaleX / (float) entitiesView.getMeasuredWidth()) * 100; + mediaEntity.mediaArea.coordinates.h = (mediaEntity.height - 2 * ((LocationView) entity).marker.pady * scaleY / (float) entitiesView.getMeasuredHeight()) * 100; + } mediaEntity.mediaArea.coordinates.rotation = -mediaEntity.rotation / Math.PI * 180; } } @@ -2240,6 +2240,14 @@ public Bitmap getBitmap(ArrayList entities, int res return bitmap; } + public boolean hasBlur() { + return renderView.getPainting().hasBlur; + } + + public Bitmap getBlurBitmap() { + return renderView.getResultBitmap(true, false); + } + @Override public void onCleanupEntities() { entitiesView.removeAllViews(); @@ -2737,6 +2745,10 @@ private PaintView.PopupButton buttonForPopup(String text, int icon, boolean sele return button; } + public void setBlurManager(BlurringShader.BlurManager blurManager) { + this.blurManager = blurManager; + } + public class PopupButton extends LinearLayout { public TextView textView; @@ -3037,17 +3049,7 @@ private void showMenuForEntity(final EntityView entityView) { } parent.addView(editView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 48)); } else if (entityView instanceof LocationView) { - TextView editView = new TextView(getContext()); - editView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); - editView.setBackground(Theme.getSelectorDrawable(false)); - editView.setGravity(Gravity.CENTER_VERTICAL); - editView.setLines(1); - editView.setSingleLine(); - editView.setEllipsize(TextUtils.TruncateAt.END); - editView.setPadding(AndroidUtilities.dp(14), 0, AndroidUtilities.dp(14), 0); - editView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - editView.setTag(1); - editView.setText(LocaleController.getString("PaintEdit", R.string.PaintEdit)); + TextView editView = createActionLayoutButton(1, LocaleController.getString("PaintEdit", R.string.PaintEdit)); editView.setOnClickListener(v -> { selectEntity(null); showLocationAlert((LocationView) entityView, (location, area) -> { @@ -3062,17 +3064,7 @@ private void showMenuForEntity(final EntityView entityView) { } if (entityView instanceof StickerView || entityView instanceof PhotoView) { - TextView flipView = new TextView(getContext()); - flipView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); - flipView.setBackground(Theme.getSelectorDrawable(false)); - flipView.setLines(1); - flipView.setSingleLine(); - flipView.setEllipsize(TextUtils.TruncateAt.END); - flipView.setGravity(Gravity.CENTER_VERTICAL); - flipView.setPadding(AndroidUtilities.dp(14), 0, AndroidUtilities.dp(14), 0); - flipView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - flipView.setTag(4); - flipView.setText(LocaleController.getString(R.string.Flip)); + TextView flipView = createActionLayoutButton(4, LocaleController.getString("Flip", R.string.Flip)); flipView.setOnClickListener(v -> { if (entityView instanceof StickerView) { ((StickerView) entityView).mirror(true); @@ -3117,6 +3109,21 @@ private void showMenuForEntity(final EntityView entityView) { }, this, Gravity.LEFT | Gravity.TOP, x, y); } + private TextView createActionLayoutButton(int tag, String title) { + TextView textView = new TextView(getContext()); + textView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); + textView.setBackground(Theme.getSelectorDrawable(false)); + textView.setGravity(Gravity.CENTER_VERTICAL); + textView.setLines(1); + textView.setSingleLine(); + textView.setEllipsize(TextUtils.TruncateAt.END); + textView.setPadding(AndroidUtilities.dp(14), 0, AndroidUtilities.dp(14), 0); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + textView.setTag(tag); + textView.setText(title); + return textView; + } + private void duplicateEntity(EntityView thisEntityView) { if (thisEntityView == null) { return; @@ -3132,7 +3139,6 @@ private void duplicateEntity(EntityView thisEntityView) { 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)); @@ -3779,7 +3785,7 @@ private void hideEmojiView() { @Override public int measureKeyboardHeight() { - return keyboardNotifier.getKeyboardHeight() - parent.getBottomPadding(false); + return keyboardNotifier.getKeyboardHeight() - parent.getBottomPadding2(); } @Override 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 index ad3722c780..a38fe938bf 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PreviewButtons.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PreviewButtons.java @@ -67,7 +67,7 @@ public PreviewButtons(Context 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_PAINT, R.drawable.media_draw, 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)); 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 index 3b3ef3adb3..53727c7ee7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PreviewView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PreviewView.java @@ -14,7 +14,7 @@ import android.net.Uri; import android.os.Build; import android.provider.MediaStore; -import android.util.Log; +import android.text.TextUtils; import android.util.Pair; import android.util.Size; import android.view.Gravity; @@ -26,26 +26,21 @@ import android.widget.FrameLayout; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.ExoPlayer; 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.messenger.MessageObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.BlurringShader; import org.telegram.ui.Components.ButtonBounce; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.PhotoFilterView; import org.telegram.ui.Components.VideoEditTextureView; import org.telegram.ui.Components.VideoPlayer; -import org.telegram.ui.Components.VideoTimelinePlayView; import java.io.File; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -61,8 +56,13 @@ public class PreviewView extends FrameLayout { private int videoWidth, videoHeight; private VideoEditTextureView textureView; public TextureView filterTextureView; + private PhotoFilterView photoFilterView; + public Runnable invalidateBlur; - private VideoTimelinePlayView videoTimelineView; + private VideoPlayer audioPlayer; + +// private VideoTimelinePlayView videoTimelineView; + private TimelineView timelineView; private final Paint snapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -72,134 +72,12 @@ public class PreviewView extends FrameLayout { 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 final BlurringShader.BlurManager blurManager; - 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); - } + public PreviewView(Context context, BlurringShader.BlurManager blurManager) { + super(context); - 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); - } - } - }); + this.blurManager = blurManager; snapPaint.setStrokeWidth(AndroidUtilities.dp(1)); snapPaint.setStyle(Paint.Style.STROKE); @@ -213,16 +91,12 @@ public long getDuration() { if (entry != null && entry.fileDuration >= 0) { return (long) (1000 * entry.fileDuration); } - if (videoPlayer != null) { + if (videoPlayer != null && videoPlayer.getDuration() != C.TIME_UNSET) { return videoPlayer.getDuration(); } return 0; } - public VideoTimelinePlayView getTimelineView() { - return videoTimelineView; - } - public void set(StoryEntry entry) { set(entry, null, 0); } @@ -234,6 +108,7 @@ public void set(StoryEntry entry, Runnable whenReady, long seekTo) { setupImage(null); setupParts(null); gradientPaint.setShader(null); + setupAudio((StoryEntry) null, false); return; } if (entry.isVideo) { @@ -251,17 +126,203 @@ public void set(StoryEntry entry, Runnable whenReady, long seekTo) { } setupParts(entry); applyMatrix(); + setupAudio(entry, false); + } + + public void setupAudio(StoryEntry entry, boolean animated) { + if (audioPlayer != null) { + audioPlayer.pause(); + audioPlayer.releasePlayer(true); + audioPlayer = null; + } + if (entry == null) { + return; + } + if (timelineView != null) { + timelineView.setAudio(entry.audioPath, entry.audioAuthor, entry.audioTitle, entry.audioDuration, entry.audioOffset, entry.audioLeft, entry.audioRight, entry.audioVolume, animated); + } + if (entry.audioPath != null) { + audioPlayer = new VideoPlayer(); + audioPlayer.allowMultipleInstances = true; + audioPlayer.setDelegate(new VideoPlayer.VideoPlayerDelegate() { + @Override + public void onStateChanged(boolean playWhenReady, int playbackState) { + AndroidUtilities.cancelRunOnUIThread(updateAudioProgressRunnable); + if (audioPlayer != null && audioPlayer.isPlaying()) { + AndroidUtilities.runOnUIThread(updateAudioProgressRunnable); + } + } + + @Override + public void onError(VideoPlayer player, Exception e) { + + } + + @Override + public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { + + } + + @Override + public void onRenderedFirstFrame() { + + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { + + } + + @Override + public boolean onSurfaceDestroyed(SurfaceTexture surfaceTexture) { + return false; + } + }); + audioPlayer.preparePlayer(Uri.fromFile(new File(entry.audioPath)), "other"); + updateAudioPlayer(true); + } + } + + public void setupAudio(MessageObject messageObject, boolean animated) { + if (entry != null) { + if (messageObject == null || messageObject.messageOwner == null) { + entry.audioPath = null; + entry.audioAuthor = null; + entry.audioTitle = null; + entry.audioDuration = entry.audioOffset = 0; + entry.audioLeft = 0; + entry.audioRight = 1; + } else { + entry.audioPath = messageObject.messageOwner.attachPath; + entry.audioAuthor = null; + entry.audioTitle = null; + TLRPC.Document audioDocument = messageObject.getDocument(); + if (audioDocument != null) { + for (TLRPC.DocumentAttribute attr : audioDocument.attributes) { + if (attr instanceof TLRPC.TL_documentAttributeAudio) { + entry.audioAuthor = attr.performer; + if (!TextUtils.isEmpty(attr.title)) { + entry.audioTitle = attr.title; + } + entry.audioDuration = (long) (attr.duration * 1000); + break; + } else if (attr instanceof TLRPC.TL_documentAttributeFilename) { + entry.audioTitle = attr.file_name; + } + } + } + entry.audioOffset = 0; + entry.audioLeft = 0; + long scrollDuration = Math.min(entry != null && entry.isVideo ? getDuration() : entry.audioDuration, TimelineView.MAX_SCROLL_DURATION); + entry.audioRight = entry.audioDuration == 0 ? 1 : Math.min(1, Math.min(scrollDuration, TimelineView.MAX_SELECT_DURATION) / (float) entry.audioDuration); + } + } + setupAudio(entry, animated); + } + + private void seekTo(long position) { + if (videoPlayer != null) { + videoPlayer.seekTo(position, false); + } else if (audioPlayer != null) { + audioPlayer.seekTo(position, false); + } + updateAudioPlayer(true); + } + + public void setVideoTimelineView(TimelineView timelineView) { + this.timelineView = timelineView; + if (timelineView != null) { + timelineView.setDelegate(new TimelineView.TimelineDelegate() { + @Override + public void onProgressDragChange(boolean dragging) { + updatePauseReason(-4, dragging); + } + + @Override + public void onProgressChange(long progress, boolean fast) { + if (!fast) { + seekTo(progress); + } else if (videoPlayer != null) { + videoPlayer.seekTo(progress, true); + } else if (audioPlayer != null) { + audioPlayer.seekTo(progress, false); + } + } + + @Override + public void onVideoLeftChange(float left) { + if (entry == null) { + return; + } + entry.left = left; + if (videoPlayer != null && videoPlayer.getDuration() != C.TIME_UNSET) { + seekTo((long) (left * videoPlayer.getDuration())); + } + } + + @Override + public void onVideoRightChange(float right) { + if (entry == null) { + return; + } + entry.right = right; + } + + @Override + public void onAudioLeftChange(float left) { + if (entry == null) { + return; + } + entry.audioLeft = left; + updateAudioPlayer(true); + } + + @Override + public void onAudioRightChange(float right) { + if (entry == null) { + return; + } + entry.audioRight = right; + updateAudioPlayer(true); + } + + @Override + public void onAudioOffsetChange(long offset) { + if (entry == null) { + return; + } + entry.audioOffset = offset; + updateAudioPlayer(true); + } + + @Override + public void onAudioRemove() { + setupAudio((MessageObject) null, true); + } + + @Override + public void onAudioVolumeChange(float volume) { + if (entry == null) { + return; + } + entry.audioVolume = volume; + if (audioPlayer != null) { + audioPlayer.setVolume(volume); + } + } + }); + } } private void setupImage(StoryEntry entry) { - if (bitmap != null) { + if (bitmap != null && !bitmap.isRecycled()) { bitmap.recycle(); - bitmap = null; } - if (thumbBitmap != null) { + bitmap = null; + if (thumbBitmap != null && !thumbBitmap.isRecycled()) { thumbBitmap.recycle(); - thumbBitmap = null; } + thumbBitmap = null; if (entry != null) { final int rw = getMeasuredWidth() <= 0 ? AndroidUtilities.displaySize.x : getMeasuredWidth(); final int rh = (int) (rw * 16 / 9f); @@ -314,58 +375,13 @@ private void setupImage(StoryEntry entry) { 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(); -// }); -// }); + if (entry != null && blurManager != null && bitmap != null) { + blurManager.resetBitmap(); + blurManager.setFallbackBlur(entry.buildBitmap(0.2f, bitmap), 0); + if (invalidateBlur != null) { + invalidateBlur.run(); + } + } return; } if (!entry.isDraft && entry.isVideo && bitmap != null) { @@ -374,6 +390,13 @@ private void setupImage(StoryEntry entry) { entry.setupMatrix(); } } + if (entry != null && blurManager != null && bitmap != null) { + blurManager.resetBitmap(); + blurManager.setFallbackBlur(entry.buildBitmap(0.2f, bitmap), 0); + if (invalidateBlur != null) { + invalidateBlur.run(); + } + } invalidate(); } @@ -382,23 +405,43 @@ private void setupGradient() { if (entry.gradientTopColor == 0 || entry.gradientBottomColor == 0) { if (bitmap != null) { DominantColors.getColors(true, bitmap, true, colors -> { - entry.gradientTopColor = colors[0]; - entry.gradientBottomColor = colors[1]; + entry.gradientTopColor = gradientTop = colors[0]; + entry.gradientBottomColor = gradientBottom = colors[1]; gradientPaint.setShader(new LinearGradient(0, 0, 0, height, colors, new float[]{0, 1}, Shader.TileMode.CLAMP)); invalidate(); + + if (textureView != null) { + textureView.updateUiBlurGradient(gradientTop, gradientBottom); + } + if (photoFilterView != null) { + photoFilterView.updateUiBlurGradient(gradientTop, gradientBottom); + } }); } else if (thumbBitmap != null) { DominantColors.getColors(true, thumbBitmap, true, colors -> { - entry.gradientTopColor = colors[0]; - entry.gradientBottomColor = colors[1]; + entry.gradientTopColor = gradientTop = colors[0]; + entry.gradientBottomColor = gradientBottom = colors[1]; gradientPaint.setShader(new LinearGradient(0, 0, 0, height, colors, new float[]{0, 1}, Shader.TileMode.CLAMP)); invalidate(); + + if (textureView != null) { + textureView.updateUiBlurGradient(gradientTop, gradientBottom); + } + if (photoFilterView != null) { + photoFilterView.updateUiBlurGradient(gradientTop, gradientBottom); + } }); } 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)); + gradientPaint.setShader(new LinearGradient(0, 0, 0, height, new int[] { gradientTop = entry.gradientTopColor, gradientBottom = entry.gradientBottomColor }, new float[]{0, 1}, Shader.TileMode.CLAMP)); + if (textureView != null) { + textureView.updateUiBlurGradient(gradientTop, gradientBottom); + } + if (photoFilterView != null) { + photoFilterView.updateUiBlurGradient(gradientTop, gradientBottom); + } } invalidate(); @@ -421,6 +464,9 @@ private void setupVideoPlayer(StoryEntry entry, Runnable whenReady, long seekTo) } }).start(); } + if (timelineView != null) { + timelineView.setVideo(null, 1); + } AndroidUtilities.cancelRunOnUIThread(updateProgressRunnable); if (whenReady != null) { AndroidUtilities.runOnUIThread(whenReady); @@ -434,6 +480,7 @@ private void setupVideoPlayer(StoryEntry entry, Runnable whenReady, long seekTo) final Runnable[] whenReadyFinal = new Runnable[] { whenReady }; videoPlayer = new VideoPlayer(); + videoPlayer.allowMultipleInstances = true; videoPlayer.setDelegate(new VideoPlayer.VideoPlayerDelegate() { @Override public void onStateChanged(boolean playWhenReady, int playbackState) { @@ -524,6 +571,8 @@ public boolean onSurfaceDestroyed(SurfaceTexture surfaceTexture) { } textureView = new VideoEditTextureView(getContext(), videoPlayer); + blurManager.resetBitmap(); + textureView.updateUiBlurManager(blurManager); textureView.setAlpha(whenReady == null ? 0f : 1f); textureView.setOpaque(false); applyMatrix(); @@ -543,12 +592,20 @@ public boolean onSurfaceDestroyed(SurfaceTexture surfaceTexture) { videoPlayer.seekTo(seekTo); } videoPlayer.setMute(entry.muted); + updateAudioPlayer(true); - videoTimelineView.setVideoPath(uri.toString(), entry.left, entry.right); + timelineView.setVideo(uri.toString(), getDuration()); + timelineView.setVideoLeft(entry.left); + timelineView.setVideoRight(entry.right); } } public long release() { + if (audioPlayer != null) { + audioPlayer.pause(); + audioPlayer.releasePlayer(true); + audioPlayer = null; + } if (videoPlayer != null) { long t = videoPlayer.getCurrentPosition(); videoPlayer.pause(); @@ -606,35 +663,99 @@ public void setupParts(StoryEntry entry) { } } - public void setFilterTextureView(TextureView view) { + public void setFilterTextureView(TextureView view, PhotoFilterView photoFilterView) { if (filterTextureView != null) { removeView(filterTextureView); filterTextureView = null; } + this.photoFilterView = photoFilterView; filterTextureView = view; + if (photoFilterView != null) { + photoFilterView.updateUiBlurGradient(gradientTop, gradientBottom); + } if (filterTextureView != null) { addView(filterTextureView); } } + private long lastPos; private long seekedLastTime; private final Runnable updateProgressRunnable = () -> { - if (videoPlayer == null) { + if (videoPlayer == null || timelineView == 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())); + + long pos = videoPlayer.getCurrentPosition(); + if (getDuration() > 0) { + final float progress = pos / (float) getDuration(); + if (!timelineView.isDragging() && (progress < entry.left || progress > entry.right) && System.currentTimeMillis() - seekedLastTime > MIN_DURATION / 2) { + seekedLastTime = System.currentTimeMillis(); + videoPlayer.seekTo(pos = (long) (entry.left * getDuration())); + updateAudioPlayer(true); + } else { + updateAudioPlayer(pos < lastPos); + } + timelineView.setProgress(videoPlayer.getCurrentPosition()); + } else { + timelineView.setProgress(videoPlayer.getCurrentPosition()); } - 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)); } + lastPos = pos; + }; + + private final Runnable updateAudioProgressRunnable = () -> { + if (audioPlayer == null || videoPlayer != null || timelineView == null) { + return; + } + + long pos = audioPlayer.getCurrentPosition(); + if (entry != null && (pos < entry.audioLeft * entry.audioDuration || pos > entry.audioRight * entry.audioDuration) && System.currentTimeMillis() - seekedLastTime > MIN_DURATION / 2) { + seekedLastTime = System.currentTimeMillis(); + audioPlayer.seekTo(pos = (long) (entry.audioLeft * entry.audioDuration)); + } + timelineView.setProgress(pos); + + if (audioPlayer.isPlaying()) { + AndroidUtilities.cancelRunOnUIThread(this.updateAudioProgressRunnable); + AndroidUtilities.runOnUIThread(this.updateAudioProgressRunnable, (long) (1000L / AndroidUtilities.screenRefreshRate)); + } }; + private void updateAudioPlayer(boolean updateSeek) { + if (audioPlayer == null || entry == null) { + return; + } + + if (videoPlayer == null) { + audioPlayer.setPlayWhenReady(pauseLinks.isEmpty()); + audioPlayer.setLooping(true); + + long pos = audioPlayer.getCurrentPosition(); + if (updateSeek && audioPlayer.getDuration() != C.TIME_UNSET) { + final float progress = pos / (float) audioPlayer.getDuration(); + if ((progress < entry.audioLeft || progress > entry.audioRight) && System.currentTimeMillis() - seekedLastTime > MIN_DURATION / 2) { + seekedLastTime = System.currentTimeMillis(); + audioPlayer.seekTo(pos = -entry.audioOffset); + } + } + return; + } + + final long pos = videoPlayer.getCurrentPosition(); + final long duration = (long) ((entry.audioRight - entry.audioLeft) * entry.audioDuration); + boolean shouldPlaying = videoPlayer.isPlaying() && pos >= entry.audioOffset && pos <= entry.audioOffset + duration; + long audioPos = pos - entry.audioOffset + (long) (entry.audioLeft * entry.audioDuration); + if (audioPlayer.isPlaying() != shouldPlaying) { + audioPlayer.setPlayWhenReady(shouldPlaying); + audioPlayer.seekTo(audioPos); + } else if (updateSeek && Math.abs(audioPlayer.getCurrentPosition() - audioPos) > 120) { + audioPlayer.seekTo(audioPos); + } + } + private Runnable onErrorListener; public void whenError(Runnable listener) { onErrorListener = listener; @@ -649,6 +770,7 @@ public void mute(boolean 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 int gradientTop, gradientBottom; private final Matrix matrix = new Matrix(); @@ -1087,6 +1209,7 @@ public void updatePauseReason(int reasonId, boolean pause) { if (videoPlayer != null) { videoPlayer.setPlayWhenReady(pauseLinks.isEmpty()); } + updateAudioPlayer(true); } // ignores actual player and other reasons to pause a video 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 index e1b163f9ef..0be5cebb5b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryEntry.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryEntry.java @@ -28,11 +28,13 @@ import org.telegram.messenger.MediaController; import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesController; 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.messenger.video.MediaCodecVideoConvertor; import org.telegram.tgnet.AbstractSerializedData; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.RequestDelegate; @@ -68,6 +70,13 @@ public class StoryEntry extends IStoryPart { public boolean isError; public TLRPC.TL_error error; + public String audioPath; + public String audioAuthor, audioTitle; + public long audioDuration; + public long audioOffset; + public float audioLeft, audioRight = 1; + public float audioVolume = 1; + public long editDocumentId; public long editPhotoId; public long editExpireDate; @@ -92,6 +101,8 @@ public class StoryEntry extends IStoryPart { public int partsMaxId = 1; public final ArrayList parts = new ArrayList<>(); + public TLRPC.InputPeer peer; + public static class Part extends IStoryPart { public File file; public boolean fileDeletable; @@ -151,6 +162,7 @@ public void serializeToStream(AbstractSerializedData stream) { // paint public File paintFile; + public File paintBlurFile; public File paintEntitiesFile; public long averageDuration = 5000; public ArrayList mediaEntities; @@ -168,6 +180,9 @@ public boolean wouldBeVideo() { if (isVideo) { return true; } + if (audioPath != null) { + return true; + } if (mediaEntities != null && !mediaEntities.isEmpty()) { for (int i = 0; i < mediaEntities.size(); ++i) { VideoEditedInfo.MediaEntity entity = mediaEntities.get(i); @@ -195,13 +210,12 @@ private boolean isAnimated(TLRPC.Document document, String path) { ); } - - public void buildPhoto(File dest) { - + public Bitmap buildBitmap(float scale, Bitmap mainFileBitmap) { 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); + final int w = (int) (resultWidth * scale), h = (int) (resultHeight * scale); + Bitmap finalBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(finalBitmap); Paint gradientPaint = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -209,63 +223,82 @@ public void buildPhoto(File dest) { 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); + if (mainFileBitmap != null) { + final float s = (float) width / mainFileBitmap.getWidth(); + tempMatrix.preScale(s, s); + tempMatrix.postScale(scale, scale); + canvas.drawBitmap(mainFileBitmap, tempMatrix, bitmapPaint); + } else { + File file = filterFile != null ? filterFile : this.file; + if (file != null) { + try { + Bitmap fileBitmap = getScaledBitmap(opts -> BitmapFactory.decodeFile(file.getPath(), opts), w, h, true); + final float s = (float) width / fileBitmap.getWidth(); + tempMatrix.preScale(s, s); + tempMatrix.postScale(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); + 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), w, h, false); + final float s = (float) part.width / fileBitmap.getWidth(); + tempMatrix.set(part.matrix); + tempMatrix.preScale(s, s); + tempMatrix.postScale(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); + if (paintFile != null) { + try { + Bitmap paintBitmap = getScaledBitmap(opts -> BitmapFactory.decodeFile(paintFile.getPath(), opts), w, h, false); + canvas.save(); + float s = resultWidth / (float) paintBitmap.getWidth(); + canvas.scale(s, s); + tempMatrix.postScale(scale, scale); + canvas.drawBitmap(paintBitmap, 0, 0, bitmapPaint); + canvas.restore(); + paintBitmap.recycle(); + } catch (Exception e) { + FileLog.e(e); + } } - } - if (paintEntitiesFile != null) { - try { - Bitmap paintBitmap = getScaledBitmap(opts -> BitmapFactory.decodeFile(paintEntitiesFile.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); + if (paintEntitiesFile != null) { + try { + Bitmap paintBitmap = getScaledBitmap(opts -> BitmapFactory.decodeFile(paintEntitiesFile.getPath(), opts), w, h, false); + canvas.save(); + float s = resultWidth / (float) paintBitmap.getWidth(); + canvas.scale(s, s); + tempMatrix.postScale(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); + return finalBitmap; + } + public void buildPhoto(File dest) { + final Bitmap finalBitmap = buildBitmap(1f, null); + if (thumbBitmap != null) { + thumbBitmap.recycle(); + thumbBitmap = null; + } + thumbBitmap = Bitmap.createScaledBitmap(finalBitmap, 40, 22, true); try { FileOutputStream stream = new FileOutputStream(dest); finalBitmap.compress(Bitmap.CompressFormat.JPEG, 95, stream); @@ -273,7 +306,6 @@ public void buildPhoto(File dest) { } catch (Exception e) { FileLog.e(e); } - finalBitmap.recycle(); } @@ -533,6 +565,7 @@ public static StoryEntry fromStoryItem(File file, TLRPC.StoryItem storyItem) { entry.setupMatrix(); entry.checkStickers(storyItem); entry.editedMediaAreas = storyItem.media_areas; + entry.peer = MessagesController.getInstance(entry.currentAccount).getInputPeer(storyItem.dialogId); return entry; } @@ -758,6 +791,7 @@ public void getVideoEditedInfo(@NonNull Utilities.Callback when 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; + info.blurPath = paintBlurFile == null ? null : paintBlurFile.getPath(); } else { if (filterFile != null) { info.originalPath = filterFile.getAbsolutePath(); @@ -765,8 +799,11 @@ public void getVideoEditedInfo(@NonNull Utilities.Callback when info.originalPath = videoPath; } info.isPhoto = true; - info.originalDuration = duration = averageDuration; - info.estimatedDuration = info.originalDuration; + if (audioPath != null) { + info.estimatedDuration = info.originalDuration = duration = (long) ((audioRight - audioLeft) * audioDuration); + } else { + info.estimatedDuration = info.originalDuration = duration = averageDuration; + } info.startTime = -1; info.endTime = -1; info.muted = true; @@ -791,6 +828,20 @@ public void getVideoEditedInfo(@NonNull Utilities.Callback when info.hdrInfo = hdrInfo; info.parts = parts; + info.mixedSoundInfos.clear(); + if (audioPath != null) { + final MediaCodecVideoConvertor.MixedSoundInfo soundInfo = new MediaCodecVideoConvertor.MixedSoundInfo(audioPath); + soundInfo.volume = audioVolume; + soundInfo.audioOffset = (long) (audioLeft * audioDuration) * 1000L; + if (isVideo) { + soundInfo.startTime = (long) (audioOffset - left * duration) * 1000L; + } else { + soundInfo.startTime = 0; + } + soundInfo.duration = (long) ((audioRight - audioLeft) * audioDuration) * 1000L; + info.mixedSoundInfos.add(soundInfo); + } + whenDone.run(info); }); }); 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 index d2c5aaca5d..73b276080f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryPrivacyBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryPrivacyBottomSheet.java @@ -1957,7 +1957,7 @@ public int getTopOffset(int tag) { viewPager = new ViewPagerFixed(context) { @Override - protected void onTabAnimationUpdate() { + protected void onTabAnimationUpdate(boolean manual) { containerView.invalidate(); } 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 index c52d13bd3e..d15837c4c4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryRecorder.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryRecorder.java @@ -9,7 +9,6 @@ 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; @@ -18,7 +17,6 @@ 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; @@ -42,7 +40,6 @@ import android.net.Uri; import android.os.Build; import android.os.Parcelable; -import android.provider.MediaStore; import android.text.Layout; import android.text.Spannable; import android.text.SpannableString; @@ -50,15 +47,11 @@ import android.text.Spanned; import android.text.TextPaint; import android.text.TextUtils; -import android.text.style.CharacterStyle; import android.text.style.ClickableSpan; import android.text.style.ForegroundColorSpan; import android.text.style.ImageSpan; import android.text.style.URLSpan; -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; @@ -69,7 +62,6 @@ 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; @@ -77,19 +69,16 @@ 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.DialogObject; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.ImageLoader; @@ -98,6 +87,7 @@ 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; @@ -105,7 +95,6 @@ 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; @@ -116,16 +105,17 @@ import org.telegram.ui.ActionBar.SimpleTextView; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.AlertsCreator; +import org.telegram.ui.Components.BlurringShader; 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.ItemOptions; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.Paint.RenderView; import org.telegram.ui.Components.PhotoFilterBlurControl; import org.telegram.ui.Components.PhotoFilterCurvesControl; import org.telegram.ui.Components.PhotoFilterView; @@ -133,13 +123,10 @@ 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.TextStyleSpan; -import org.telegram.ui.Components.URLSpanReplacement; 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.PremiumPreviewFragment; @@ -155,7 +142,6 @@ 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 { @@ -683,8 +669,13 @@ public WindowView(Context context) { private Rect rect = new Rect(); private int lastKeyboardHeight; - public int getBottomPadding(boolean withUnderControls) { - return getHeight() - containerView.getBottom() + (withUnderControls ? underControls : 0); + @Override + public int getBottomPadding() { + return getHeight() - containerView.getBottom() + underControls; + } + + public int getBottomPadding2() { + return getHeight() - containerView.getBottom(); } public int getPaddingUnderContainer() { @@ -1069,11 +1060,13 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } } - if (paintView != null && paintView.emojiView != null) { - paintView.emojiView.measure( - MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(paintView.emojiView.getLayoutParams().height, MeasureSpec.EXACTLY) - ); + if (paintView != null) { + if (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) { @@ -1134,8 +1127,10 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto } } - if (paintView != null && paintView.emojiView != null) { - paintView.emojiView.layout(insetLeft, H - insetBottom - paintView.emojiView.getMeasuredHeight(), W - insetRight, H - insetBottom); + if (paintView != null) { + if (paintView.emojiView != null) { + paintView.emojiView.layout(insetLeft, H - insetBottom - paintView.emojiView.getMeasuredHeight(), W - insetRight, H - insetBottom); + } } for (int i = 0; i < getChildCount(); ++i) { @@ -1265,6 +1260,13 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto final int w = right - left; final int h = bottom - top; + for (int i = 0; i < getChildCount(); ++i) { + View child = getChildAt(i); + if (child instanceof ItemOptions.DimView) { + child.layout(0, 0, w, h); + } + } + setPivotX((right - left) / 2f); setPivotY(-h * .2f); } @@ -1275,6 +1277,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int H = MeasureSpec.getSize(heightMeasureSpec); measureChildExactly(previewContainer, previewW, previewH); + applyFilterMatrix(); measureChildExactly(actionBarContainer, previewW, dp(56 + 56 + 38)); measureChildExactly(controlContainer, previewW, dp(220)); measureChildExactly(navbarContainer, previewW, underControls); @@ -1289,6 +1292,14 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (paintView != null) { measureChildExactly(paintView, W, H); } + + for (int i = 0; i < getChildCount(); ++i) { + View child = getChildAt(i); + if (child instanceof ItemOptions.DimView) { + measureChildExactly(child, W, H); + } + } + setMeasuredDimension(W, H); } @@ -1333,6 +1344,7 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { private ImageView backButton; private SimpleTextView titleTextView; private StoryPrivacyBottomSheet privacySheet; + private BlurringShader.BlurManager blurManager; /* PAGE_CAMERA */ private ImageView cameraViewThumb; @@ -1355,10 +1367,10 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { /* PAGE_PREVIEW */ private PreviewView previewView; private FrameLayout videoTimelineContainerView; - private VideoTimelinePlayView videoTimelineView; + private TimelineView timelineView; private VideoTimeView videoTimeView; private PreviewButtons previewButtons; - private CaptionContainerView captionEdit; + private CaptionStory captionEdit; private DownloadButton downloadButton; private RLottieDrawable muteButtonDrawable; private RLottieImageView muteButton; @@ -1373,16 +1385,14 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { private TrashView trash; /* EDIT_MODE_PAINT */ - private Bitmap paintViewBitmap; private PaintView paintView; - private View paintViewRenderView; + private RenderView 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; @@ -1468,6 +1478,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } } }); + blurManager = new BlurringShader.BlurManager(previewContainer); containerView.addView(actionBarContainer = new FrameLayout(context)); // 150dp containerView.addView(controlContainer = new FrameLayout(context)); // 220dp containerView.addView(captionContainer = new FrameLayout(context) { @@ -1521,7 +1532,7 @@ public void getOutline(View view, Outline outline) { previewContainer.setClipToOutline(true); } photoFilterEnhanceView = new PhotoFilterView.EnhanceView(context, this::createFilterPhotoView); - previewView = new PreviewView(context) { + previewView = new PreviewView(context, blurManager) { @Override public boolean additionalTouchEvent(MotionEvent ev) { return photoFilterEnhanceView.onTouch(ev); @@ -1532,6 +1543,7 @@ public void applyMatrix() { super.applyMatrix(); applyFilterMatrix(); } + @Override public void onEntityDraggedTop(boolean value) { previewHighlight.show(true, value, actionBarContainer); @@ -1580,10 +1592,14 @@ protected void onTimeDrag(boolean dragStart, long time, boolean dragEnd) { videoTimeView.show(!dragEnd, true); } }; + previewView.invalidateBlur = this::invalidateBlur; previewView.setOnTapListener(() -> { if (currentEditMode != EDIT_MODE_NONE || currentPage != PAGE_PREVIEW || captionEdit.keyboardShown) { return; } + if (timelineView.onBackPressed()) { + return; + } switchToEditMode(EDIT_MODE_PAINT, true); if (paintView != null) { paintView.openText(); @@ -1600,7 +1616,7 @@ protected void onTimeDrag(boolean dragStart, long time, boolean dragEnd) { previewContainer.addView(photoFilterEnhanceView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); - captionEdit = new CaptionContainerView(context, currentAccount, windowView, containerView, resourcesProvider) { + captionEdit = new CaptionStory(context, windowView, windowView, containerView, resourcesProvider, blurManager) { @Override protected void drawBlurBitmap(Bitmap bitmap, float amount) { windowView.drawBlurBitmap(bitmap, amount); @@ -1651,6 +1667,8 @@ protected void onCaptionLimitUpdate(boolean overLimit) { previewButtons.setShareEnabled(!videoError && !overLimit && (!MessagesController.getInstance(currentAccount).getStoriesController().hasStoryLimit() || (outputEntry != null && outputEntry.isEdit))); } }; + captionEdit.setAccount(currentAccount); + captionEdit.setUiBlurBitmap(this::getUiBlurBitmap); Bulletin.addDelegate(captionContainer, new Bulletin.Delegate() { @Override public int getBottomOffset(int tag) { @@ -1675,6 +1693,9 @@ public int getBottomOffset(int tag) { }); captionEdit.setOnPremiumHint(this::showPremiumPeriodBulletin); captionEdit.setOnKeyboardOpen(open -> { + if (open && timelineView != null) { + timelineView.onBackPressed(); + } previewView.updatePauseReason(2, open); videoTimelineContainerView.clearAnimation(); videoTimelineContainerView.animate().alpha(open ? 0f : 1f).setDuration(120).start(); @@ -1684,16 +1705,17 @@ public int getBottomOffset(int tag) { } }); - videoTimelineView = previewView.getTimelineView(); - videoTimelineView.setVisibility(View.GONE); - videoTimelineView.setAlpha(0f); + timelineView = new TimelineView(context, containerView, previewContainer, resourcesProvider, blurManager); + previewView.setVideoTimelineView(timelineView); + timelineView.setVisibility(View.GONE); + timelineView.setAlpha(0f); videoTimelineContainerView = new FrameLayout(context); - videoTimelineContainerView.addView(videoTimelineView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 58, Gravity.FILL_HORIZONTAL | Gravity.BOTTOM, 0, 0, 0, 0)); + videoTimelineContainerView.addView(timelineView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 80, 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(videoTimelineContainerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 80 + 25, Gravity.FILL_HORIZONTAL | Gravity.BOTTOM, 0, 0, 0, 68)); captionContainer.addView(captionEdit, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL_HORIZONTAL | Gravity.BOTTOM, 0, 200, 0, 0)); backButton = new ImageView(context); @@ -1747,7 +1769,13 @@ public int getBottomOffset(int tag) { return; } outputEntry.muted = !outputEntry.muted; - muteHint.setText(outputEntry.muted ? LocaleController.getString("StorySoundMuted") : LocaleController.getString("StorySoundNotMuted"), muteHint.shown()); + final boolean hasMusic = !TextUtils.isEmpty(outputEntry.audioPath); + muteHint.setText( + outputEntry.muted ? + LocaleController.getString(hasMusic ? R.string.StoryOriginalSoundMuted : R.string.StorySoundMuted) : + LocaleController.getString(hasMusic ? R.string.StoryOriginalSoundNotMuted : R.string.StorySoundNotMuted), + muteHint.shown() + ); muteHint.show(); setIconMuted(outputEntry.muted, true); previewView.mute(outputEntry.muted); @@ -1989,6 +2017,17 @@ public int getBottomOffset(int tag) { previewContainer.addView(previewHighlight, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); } + private Bitmap getUiBlurBitmap() { + Bitmap blur = null; + if (photoFilterView != null) { + blur = photoFilterView.getUiBlurBitmap(); + } + if (blur == null && previewView != null && previewView.getTextureView() != null) { + blur = previewView.getTextureView().getUiBlurBitmap(); + } + return blur; + } + private ArrayList getUsersFrom(CharSequence caption) { ArrayList users = new ArrayList<>(); if (caption instanceof Spanned) { @@ -2042,8 +2081,22 @@ private DraftSavedHint getDraftSavedHint() { return draftSavedHint; } + private boolean preparingUpload = false; private void upload(boolean asStory) { - applyPaint(); + if (preparingUpload) { + return; + } + preparingUpload = true; + Utilities.globalQueue.postRunnable(() -> { + applyPaint(); + AndroidUtilities.runOnUIThread(() -> { + preparingUpload = false; + uploadInternal(asStory); + }); + }); + } + + private void uploadInternal(boolean asStory) { if (outputEntry == null) { close(true); return; @@ -2058,12 +2111,18 @@ private void upload(boolean asStory) { MessagesController.getInstance(currentAccount).getStoriesController().getDraftsController().delete(outputEntry); } outputEntry.cancelCheckStickers(); + + long sendAsDialogId = UserConfig.getInstance(currentAccount).clientUserId; + if (outputEntry.peer != null && !(outputEntry.peer instanceof TLRPC.TL_inputPeerSelf)) { + sendAsDialogId = DialogObject.getPeerDialogId(outputEntry.peer); + } outputEntry = null; wasSend = true; forceBackgroundVisible = true; checkBackgroundVisibility(); + long finalSendAsDialogId = sendAsDialogId; Runnable runnable = () -> { if (asStory) { if (fromSourceView != null) { @@ -2145,6 +2204,20 @@ private File prepareThumb(StoryEntry storyEntry, boolean forDraft) { previewTextureView.recycle(); } + if (storyEntry.paintBlurFile != null) { + try { + Bitmap paintBitmap = BitmapFactory.decodeFile(storyEntry.paintBlurFile.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 (storyEntry.paintFile != null) { try { Bitmap paintBitmap = BitmapFactory.decodeFile(storyEntry.paintFile.getPath()); @@ -2376,7 +2449,7 @@ public void onVideoRecordStart(boolean byLongPress, Runnable whenStarted) { videoTimerView.setRecording(true, true); showVideoTimer(true, true); - }, cameraView, false); + }, cameraView, true); if (!isVideo) { isVideo = true; @@ -2624,7 +2697,9 @@ public boolean onBackPressed() { 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()) { + if (paintView != null && paintView.onBackPressed()){ + return false; + } else if (fromGallery && (paintView == null || !paintView.hasChanges()) && (outputEntry == null || outputEntry.filterFile == null) || !previewButtons.isShareEnabled()) { navigateTo(PAGE_CAMERA, true); } else { showDismissEntry(); @@ -2657,6 +2732,7 @@ public void navigateToPreviewWithPlayerAwait(Runnable open, long seekTo) { previewView.setAlpha(0f); previewView.setVisibility(View.VISIBLE); previewView.set(outputEntry, afterPlayerAwait, seekTo); + previewView.setupAudio(outputEntry, false); AndroidUtilities.runOnUIThread(afterPlayerAwait, 400); } @@ -2706,9 +2782,10 @@ public void navigateTo(int page, boolean animated) { 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(timelineView, View.ALPHA, page == PAGE_PREVIEW ? 1f : 0)); - animators.add(ObjectAnimator.ofFloat(muteButton, View.ALPHA, page == PAGE_PREVIEW && isVideo ? 1f : 0)); + animators.add(ObjectAnimator.ofFloat(muteButton, View.ALPHA, page == PAGE_PREVIEW && (isVideo || outputEntry != null && !TextUtils.isEmpty(outputEntry.audioPath)) ? 1f : 0)); + ((ViewGroup.MarginLayoutParams) playButton.getLayoutParams()).rightMargin = dp(48 + (isVideo ? 48 : 0)); animators.add(ObjectAnimator.ofFloat(playButton, 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)); @@ -2743,11 +2820,12 @@ public void onAnimationEnd(Animator animation) { 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); + muteButton.setAlpha(page == PAGE_PREVIEW && (isVideo || outputEntry != null && !TextUtils.isEmpty(outputEntry.audioPath)) ? 1f : 0); + ((ViewGroup.MarginLayoutParams) playButton.getLayoutParams()).rightMargin = dp(48 + (isVideo ? 48 : 0)); playButton.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); + timelineView.setAlpha(page == PAGE_PREVIEW ? 1f : 0); titleTextView.setAlpha(page == PAGE_PREVIEW ? 1f : 0f); onNavigateEnd(oldPage, page); } @@ -3080,6 +3158,7 @@ private void onNavigateStart(int fromPage, int toPage) { // privacySelector.set(outputEntry, false); if (!previewAlreadySet) { previewView.set(outputEntry); + previewView.setupAudio(outputEntry, false); } previewAlreadySet = false; captionEdit.editText.getEditText().setOnPremiumMenuLockClickListener(MessagesController.getInstance(currentAccount).storyEntitiesAllowed() ? null : () -> { @@ -3112,7 +3191,7 @@ private void onNavigateStart(int fromPage, int toPage) { previewButtons.setShareEnabled(!videoError && !captionEdit.isCaptionOverLimit() && (!MessagesController.getInstance(currentAccount).getStoriesController().hasStoryLimit() || (outputEntry != null && outputEntry.isEdit))); 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); + timelineView.setVisibility(View.VISIBLE); 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); @@ -3158,7 +3237,7 @@ private void onNavigateEnd(int fromPage, int toPage) { downloadButton.setVisibility(View.GONE); // privacySelector.setVisibility(View.GONE); previewView.setVisibility(View.GONE); - videoTimelineView.setVisibility(View.GONE); + timelineView.setVisibility(View.GONE); destroyPhotoPaintView(); destroyPhotoFilterView(); titleTextView.setVisibility(View.GONE); @@ -3171,6 +3250,9 @@ private void onNavigateEnd(int fromPage, int toPage) { createPhotoPaintView(); hidePhotoPaintView(); createFilterPhotoView(); + if (photoFilterEnhanceView != null) { + photoFilterEnhanceView.setAllowTouch(false); + } previewView.updatePauseReason(2, false); previewView.updatePauseReason(3, false); previewView.updatePauseReason(4, false); @@ -3234,19 +3316,11 @@ public void switchToEditMode(int editMode, boolean animated) { animators.add(ObjectAnimator.ofFloat(toolsView, View.TRANSLATION_Y, 0)); animators.add(ObjectAnimator.ofFloat(toolsView, View.ALPHA, 1)); } - TextureView textureView = photoFilterView != null ? photoFilterView.getMyTextureView() : null; - 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) { @@ -3268,7 +3342,7 @@ public void switchToEditMode(int editMode, boolean animated) { 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(muteButton, View.ALPHA, editMode == EDIT_MODE_NONE && (isVideo || outputEntry != null && !TextUtils.isEmpty(outputEntry.audioPath)) ? 1 : 0)); animators.add(ObjectAnimator.ofFloat(playButton, 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)); @@ -3351,16 +3425,27 @@ private void createPhotoPaintView() { return; } Pair size = previewView.getPaintSize(); - if (paintViewBitmap != null) { - paintViewBitmap.recycle(); - paintViewBitmap = null; - } + + Bitmap paintViewBitmap = null; if (outputEntry != null && (outputEntry.isDraft || outputEntry.isEdit) && outputEntry.paintFile != null) { paintViewBitmap = BitmapFactory.decodeFile(outputEntry.paintFile.getPath()); } if (paintViewBitmap == null) { paintViewBitmap = Bitmap.createBitmap(size.first, size.second, Bitmap.Config.ARGB_8888); } + + boolean hasBlur = false; + Bitmap paintViewBlurBitmap = null; + if (outputEntry != null && (outputEntry.isDraft || outputEntry.isEdit) && outputEntry.paintBlurFile != null) { + paintViewBlurBitmap = BitmapFactory.decodeFile(outputEntry.paintBlurFile.getPath()); + if (paintViewBlurBitmap != null) { + hasBlur = true; + } + } + if (paintViewBlurBitmap == null) { + paintViewBlurBitmap = Bitmap.createBitmap(size.first, size.second, Bitmap.Config.ALPHA_8); + } + int w = previewContainer.getMeasuredWidth(), h = previewContainer.getMeasuredHeight(); paintView = new PaintView( activity, @@ -3371,12 +3456,14 @@ private void createPhotoPaintView() { activity, currentAccount, paintViewBitmap, + paintViewBlurBitmap, null, previewView.getOrientation(), outputEntry == null ? null : outputEntry.mediaEntities, w, h, new MediaController.CropState(), null, + blurManager, resourcesProvider ) { @Override @@ -3469,16 +3556,26 @@ public void dismiss() { protected void onOpenCloseStickersAlert(boolean open) { if (previewView != null) { previewView.updatePauseReason(6, open); + if (playButton != null) { + playButton.drawable.setPause(previewView.isPlaying(), true); + } } if (captionEdit != null) { captionEdit.ignoreTouches = open; captionEdit.keyboardNotifier.ignore(open); } } + + @Override + public void onEntityHandleTouched() { + + } }; + paintView.setBlurManager(blurManager); containerView.addView(paintView); paintViewRenderView = paintView.getRenderView(); if (paintViewRenderView != null) { + paintViewRenderView.getPainting().hasBlur = hasBlur; previewContainer.addView(paintViewRenderView); } paintViewRenderInputView = paintView.getRenderInputView(); @@ -3548,10 +3645,6 @@ private void destroyPhotoPaintView() { paintView.shutdown(); containerView.removeView(paintView); - if (paintViewBitmap != null) { - paintViewBitmap.recycle(); - paintViewBitmap = null; - } paintView = null; if (paintViewRenderView != null) { previewContainer.removeView(paintViewRenderView); @@ -3589,7 +3682,7 @@ private void onSwitchEditModeStart(int fromMode, int toMode) { muteButton.setVisibility(View.VISIBLE); playButton.setVisibility(View.VISIBLE); } - videoTimelineView.setVisibility(View.VISIBLE); + timelineView.setVisibility(View.VISIBLE); } if (toMode == EDIT_MODE_PAINT && paintView != null) { paintView.setVisibility(View.VISIBLE); @@ -3614,9 +3707,6 @@ private void onSwitchEditModeStart(int fromMode, int toMode) { } 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); } @@ -3629,7 +3719,7 @@ private void onSwitchEditModeEnd(int fromMode, int toMode) { playButton.setVisibility(View.GONE); downloadButton.setVisibility(View.GONE); // privacySelector.setVisibility(View.GONE); - videoTimelineView.setVisibility(View.GONE); + timelineView.setVisibility(View.GONE); titleTextView.setVisibility(View.GONE); } previewView.setAllowCropping(toMode == EDIT_MODE_NONE); @@ -3654,15 +3744,17 @@ private void applyPaint() { } else { outputEntry.mediaEntities.clear(); } - paintView.getBitmap(outputEntry.mediaEntities, outputEntry.resultWidth, outputEntry.resultHeight, false, false); + paintView.getBitmap(outputEntry.mediaEntities, outputEntry.resultWidth, outputEntry.resultHeight, false, false, false); if (!outputEntry.isVideo) { outputEntry.averageDuration = Utilities.clamp(paintView.getLcm(), 7500L, 5000L); } List masks = paintView.getMasks(); outputEntry.stickers = masks != null ? new ArrayList<>(masks) : null; + final boolean isVideo = outputEntry.isVideo; + final boolean wouldBeVideo = outputEntry.wouldBeVideo(); outputEntry.mediaEntities = new ArrayList<>(); - Bitmap bitmap = paintView.getBitmap(outputEntry.mediaEntities, outputEntry.resultWidth, outputEntry.resultHeight, true, false); + Bitmap bitmap = paintView.getBitmap(outputEntry.mediaEntities, outputEntry.resultWidth, outputEntry.resultHeight, true, false, !isVideo); if (outputEntry.mediaEntities.isEmpty()) { outputEntry.mediaEntities = null; } @@ -3677,18 +3769,37 @@ private void applyPaint() { outputEntry.paintEntitiesFile.delete(); } } catch (Exception ignore) {} + try { + if (outputEntry.paintBlurFile != null) { + outputEntry.paintBlurFile.delete(); + } + } catch (Exception ignore) {} + outputEntry.paintFile = null; + outputEntry.paintEntitiesFile = null; + outputEntry.paintBlurFile = null; outputEntry.paintFile = FileLoader.getInstance(currentAccount).getPathToAttach(ImageLoader.scaleAndSaveImage(bitmap, Bitmap.CompressFormat.PNG, outputEntry.resultWidth, outputEntry.resultHeight, 87, false, 101, 101), true); - if (bitmap != null) { + if (bitmap != null && !bitmap.isRecycled()) { bitmap.recycle(); } + bitmap = null; - if (!outputEntry.wouldBeVideo()) { - bitmap = paintView.getBitmap(new ArrayList<>(), outputEntry.resultWidth, outputEntry.resultHeight, false, true); + if (!wouldBeVideo) { + bitmap = paintView.getBitmap(new ArrayList<>(), outputEntry.resultWidth, outputEntry.resultHeight, false, true, false); outputEntry.paintEntitiesFile = FileLoader.getInstance(currentAccount).getPathToAttach(ImageLoader.scaleAndSaveImage(bitmap, Bitmap.CompressFormat.PNG, outputEntry.resultWidth, outputEntry.resultHeight, 87, false, 101, 101), true); - if (bitmap != null) { + if (bitmap != null && !bitmap.isRecycled()) { + bitmap.recycle(); + } + bitmap = null; + } + + if (paintView.hasBlur()) { + bitmap = paintView.getBlurBitmap(); + outputEntry.paintBlurFile = FileLoader.getInstance(currentAccount).getPathToAttach(ImageLoader.scaleAndSaveImage(bitmap, Bitmap.CompressFormat.PNG, outputEntry.resultWidth, outputEntry.resultHeight, 87, false, 101, 101), true); + if (bitmap != null && !bitmap.isRecycled()) { bitmap.recycle(); } + bitmap = null; } } @@ -3718,18 +3829,14 @@ private void createFilterPhotoView() { 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); + photoBitmap = 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); + photoFilterView = new PhotoFilterView(activity, previewView.getTextureView(), photoBitmap, previewView.getOrientation(), outputEntry == null ? null : outputEntry.filterState, null, 0, false, false, blurManager, resourcesProvider); containerView.addView(photoFilterView); if (photoFilterEnhanceView != null) { photoFilterEnhanceView.setFilterView(photoFilterView); @@ -3738,7 +3845,7 @@ private void createFilterPhotoView() { if (photoFilterViewTextureView != null) { photoFilterViewTextureView.setOpaque(false); } - previewView.setFilterTextureView(photoFilterViewTextureView); + previewView.setFilterTextureView(photoFilterViewTextureView, photoFilterView); if (photoFilterViewTextureView != null) { photoFilterViewTextureView.setAlpha(0f); photoFilterViewTextureView.animate().alpha(1f).setDuration(220).start(); @@ -3755,23 +3862,10 @@ private void createFilterPhotoView() { 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); -// } + switchToEditMode(EDIT_MODE_NONE, true); }); photoFilterView.getToolsView().setVisibility(View.GONE); photoFilterView.getToolsView().setAlpha(0f); @@ -3779,8 +3873,14 @@ private void createFilterPhotoView() { photoFilterView.init(); } + public void invalidateBlur() { + if (captionEdit != null) { + captionEdit.invalidateBlur(); + } + } + private void applyFilterMatrix() { - if (outputEntry != null && photoFilterViewTextureView != null) { + if (outputEntry != null && photoFilterViewTextureView != null && previewContainer.getMeasuredWidth() > 0 && previewContainer.getMeasuredHeight() > 0) { Matrix photoFilterStartMatrix = new Matrix(); photoFilterStartMatrix.reset(); if (outputEntry.orientation != 0) { @@ -3808,66 +3908,6 @@ private void applyFilterMatrix() { } } -// 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; @@ -3879,7 +3919,7 @@ private void destroyPhotoFilterView() { previewContainer.removeView(photoFilterViewTextureView); photoFilterViewTextureView = null; } - previewView.setFilterTextureView(null); + previewView.setFilterTextureView(null, null); if (photoFilterViewBlurControl != null) { previewContainer.removeView(photoFilterViewBlurControl); photoFilterViewBlurControl = null; @@ -3889,10 +3929,6 @@ private void destroyPhotoFilterView() { photoFilterViewCurvesControl = null; } photoFilterView = null; - if (photoFilterBitmap != null) { - photoFilterBitmap.recycle(); - photoFilterBitmap = null; - } // photoFilterStartMatrix = null; // photoFilterEndMatrix = null; // if (photoFilterAnimator != null) { @@ -4349,7 +4385,7 @@ public boolean presentFragment(BaseFragment fragment) { openPremium(); return false; } - }, activity, storyLimit.getLimitReachedType(), currentAccount); + }, activity, storyLimit.getLimitReachedType(), currentAccount, null); sheet.setOnDismissListener(e -> { shownLimitReached = false; previewView.updatePauseReason(7, true); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/TimelineView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/TimelineView.java new file mode 100644 index 0000000000..6269e26760 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/TimelineView.java @@ -0,0 +1,1527 @@ +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.content.Context; +import android.graphics.Bitmap; +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.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Region; +import android.graphics.Shader; +import android.graphics.drawable.Drawable; +import android.media.MediaCodec; +import android.media.MediaExtractor; +import android.media.MediaFormat; +import android.media.MediaMetadataRetriever; +import android.os.Build; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.FileLog; +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.BlurringShader; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.ItemOptions; +import org.telegram.ui.Components.Scroller; + +import java.nio.ByteBuffer; +import java.util.ArrayList; + +public class TimelineView extends View { + + // milliseconds when timeline goes out of the box + public static final long MAX_SCROLL_DURATION = 120 * 1000L; + // minimal allowed duration to select + public static final long MIN_SELECT_DURATION = 1 * 1000L; + // maximum allowed duration to select + public static final long MAX_SELECT_DURATION = (long) (59 * 1000L); + + interface TimelineDelegate { + void onProgressDragChange(boolean dragging); + void onProgressChange(long progress, boolean fast); + + void onVideoLeftChange(float left); + void onVideoRightChange(float right); + + void onAudioOffsetChange(long offset); + void onAudioLeftChange(float left); + void onAudioRightChange(float right); + + void onAudioVolumeChange(float volume); + void onAudioRemove(); + } + + private TimelineDelegate delegate; + + private long progress; + private long scroll; + + private boolean hasVideo; + private String videoPath; + private long videoDuration; + private float videoLeft; + private float videoRight; + private VideoThumbsLoader thumbs; + + private boolean hasAudio; + private String audioPath; + private boolean audioSelected; + private long audioOffset; + private long audioDuration; + private float audioLeft; + private float audioRight; + private boolean waveformIsLoaded; + private float audioVolume; + private AudioWaveformLoader waveform; + + private long getBaseDuration() { + if (hasVideo) { + return videoDuration; + } + if (hasAudio) { + return audioDuration; + } + return Math.max(1, audioDuration); + } + + private final AnimatedFloat audioT = new AnimatedFloat(this, 0, 360, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat audioSelectedT = new AnimatedFloat(this, 360, CubicBezierInterpolator.EASE_OUT_QUINT); + + private final AnimatedFloat waveformLoaded = new AnimatedFloat(this, 0, 600, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat waveformMax = new AnimatedFloat(this, 0, 360, CubicBezierInterpolator.EASE_OUT_QUINT); + + private final BlurringShader.BlurManager blurManager; + private final BlurringShader.StoryBlurDrawer backgroundBlur; + private final BlurringShader.StoryBlurDrawer audioBlur; + private final BlurringShader.StoryBlurDrawer audioWaveformBlur; + + private final RectF videoBounds = new RectF(); + private final Paint videoFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + private final Path videoClipPath = new Path(); + private final Path selectedVideoClipPath = new Path(); + + private final Paint regionPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint regionCutPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint regionHandlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint progressShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint progressWhitePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private final RectF audioBounds = new RectF(); + private final Path audioClipPath = new Path(); + private final Paint waveformPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Path waveformPath = new Path(); + + private final Paint audioDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Drawable audioIcon; + private final TextPaint audioAuthorPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private StaticLayout audioAuthor; + private float audioAuthorWidth, audioAuthorLeft; + private final TextPaint audioTitlePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private StaticLayout audioTitle; + private float audioTitleWidth, audioTitleLeft; + + private final LinearGradient ellipsizeGradient = new LinearGradient(0, 0, 16, 0, new int[] { 0x00ffffff, 0xffffffff }, new float[] { 0, 1 }, Shader.TileMode.CLAMP); + private final Matrix ellipsizeMatrix = new Matrix(); + private final Paint ellipsizePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private final Scroller scroller = new Scroller(getContext()); + + private final ViewGroup container; + private final View previewContainer; + private final Theme.ResourcesProvider resourcesProvider; + + private final Runnable onLongPress; + + public TimelineView(Context context, ViewGroup container, View previewContainer, Theme.ResourcesProvider resourcesProvider, BlurringShader.BlurManager blurManager) { + super(context); + + this.container = container; + this.previewContainer = previewContainer; + this.resourcesProvider = resourcesProvider; + + audioDotPaint.setColor(0x7fffffff); + audioAuthorPaint.setTextSize(dp(12)); + audioAuthorPaint.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + audioAuthorPaint.setColor(0xffffffff); + audioTitlePaint.setTextSize(dp(12)); + audioTitlePaint.setColor(0xffffffff); + waveformPaint.setColor(0x40ffffff); + + ellipsizePaint.setShader(ellipsizeGradient); + ellipsizePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + + regionPaint.setColor(0xffffffff); + regionPaint.setShadowLayer(dp(1), 0, dp(1), 0x1a000000); + regionCutPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + regionHandlePaint.setColor(0xff000000); + progressWhitePaint.setColor(0xffffffff); + progressShadowPaint.setColor(0x26000000); + + audioIcon = getContext().getResources().getDrawable(R.drawable.filled_widget_music).mutate(); + audioIcon.setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.SRC_IN)); + + this.blurManager = blurManager; + backgroundBlur = new BlurringShader.StoryBlurDrawer(blurManager, this, BlurringShader.StoryBlurDrawer.BLUR_TYPE_BACKGROUND); + audioBlur = new BlurringShader.StoryBlurDrawer(blurManager, this, BlurringShader.StoryBlurDrawer.BLUR_TYPE_AUDIO_BACKGROUND); + audioWaveformBlur = new BlurringShader.StoryBlurDrawer(blurManager, this, BlurringShader.StoryBlurDrawer.BLUR_TYPE_AUDIO_WAVEFORM_BACKGROUND); + + onLongPress = () -> { + if (!pressVideo && hasAudio) { + VolumeSliderView slider = + new VolumeSliderView(getContext()) + .setVolume(audioVolume) + .setOnValueChange(volume -> { + audioVolume = volume; + if (delegate != null) { + delegate.onAudioVolumeChange(volume); + } + }); + final long videoScrollDuration = Math.min(getBaseDuration(), MAX_SCROLL_DURATION); + float uiRight = Math.min(w - px - ph, px + ph + (audioOffset - scroll + lerp(audioRight, 1, audioSelectedT.get()) * audioDuration) / (float) videoScrollDuration * sw); + ItemOptions itemOptions = ItemOptions.makeOptions(container, resourcesProvider, this) + .addView(slider) + .addSpaceGap() + .add(R.drawable.msg_delete, LocaleController.getString(R.string.StoryAudioRemove), () -> { + if (delegate != null) { + delegate.onAudioRemove(); + } + }) + .setGravity(Gravity.RIGHT) + .forceTop(true) + .translate(dp(6) - (w - uiRight), dp(4) + (!hasVideo ? dp(audioSelected ? 35 : 40) : 0)) + .show(); + itemOptions.setBlurBackground(blurManager, -previewContainer.getX(), -previewContainer.getY()); + + try { + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } catch (Exception e) {} + } + }; + } + + public void setDelegate(TimelineDelegate delegate) { + this.delegate = delegate; + } + + public void setVideo(String videoPath, long videoDuration) { + if (TextUtils.equals(this.videoPath, videoPath)) { + return; + } + if (thumbs != null) { + thumbs.destroy(); + thumbs = null; + } + if (videoPath != null) { + scroll = 0; + this.videoPath = videoPath; + this.videoDuration = videoDuration; + setupVideoThumbs(); + } else { + this.videoPath = null; + this.videoDuration = 1; + scroll = 0; + } + hasVideo = this.videoPath != null; + progress = 0; + invalidate(); + } + + private void setupVideoThumbs() { + if (getMeasuredWidth() <= 0 || this.thumbs != null) { + return; + } + this.thumbs = new VideoThumbsLoader(videoPath, getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), dp(38)); + if (this.thumbs.getDuration() > 0) { + videoDuration = this.thumbs.getDuration(); + } + } + + private final AnimatedFloat loopProgress = new AnimatedFloat(0, this, 0, 200, CubicBezierInterpolator.EASE_BOTH); + public void setProgress(long progress) { + if ( + hasVideo && progress < this.progress && progress <= videoDuration * videoLeft + 240 && this.progress + 240 >= videoDuration * videoRight || + hasAudio && progress < this.progress && progress <= audioDuration * audioLeft + 240 && this.progress + 240 >= audioDuration * audioRight + ) { + loopProgress.set(1, true); + } + this.progress = progress; + invalidate(); + } + + public void setVideoLeft(float left) { + videoLeft = left; + invalidate(); + } + + public void setVideoRight(float right) { + videoRight = right; + invalidate(); + } + + public void setAudio(String audioPath, String audioAuthorText, String audioTitleText, long duration, long offset, float left, float right, float volume, boolean animated) { + this.audioPath = audioPath; + if (waveform != null) { + waveform.destroy(); + waveform = null; + waveformIsLoaded = false; + waveformLoaded.set(0, true); + } + setupAudioWaveform(); + hasAudio = !TextUtils.isEmpty(audioPath); + if (!hasAudio) { + audioSelected = false; + audioAuthorText = null; + audioTitleText = null; + } + if (TextUtils.isEmpty(audioAuthorText)) { + audioAuthorText = null; + } + if (TextUtils.isEmpty(audioTitleText)) { + audioTitleText = null; + } + if (hasAudio) { + audioDuration = duration; + audioOffset = offset - (long) (left * duration); + audioLeft = left; + audioRight = right; + audioVolume = volume; + if (audioAuthorText != null) { + audioAuthor = new StaticLayout(audioAuthorText, audioAuthorPaint, 99999, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false); + audioAuthorWidth = audioAuthor.getLineCount() > 0 ? audioAuthor.getLineWidth(0) : 0; + audioAuthorLeft = audioAuthor.getLineCount() > 0 ? audioAuthor.getLineLeft(0) : 0; + } else { + audioAuthorWidth = 0; + audioAuthor = null; + } + if (audioTitleText != null) { + audioTitle = new StaticLayout(audioTitleText, audioTitlePaint, 99999, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false); + audioTitleWidth = audioTitle.getLineCount() > 0 ? audioTitle.getLineWidth(0) : 0; + audioTitleLeft = audioTitle.getLineCount() > 0 ? audioTitle.getLineLeft(0) : 0; + } else { + audioTitleWidth = 0; + audioTitle = null; + } + } + if (!animated) { + audioT.set(hasAudio, true); + } + invalidate(); + } + + private void setupAudioWaveform() { + if (getMeasuredWidth() <= 0 || this.waveform != null) { + return; + } + this.waveform = new AudioWaveformLoader(audioPath, getMeasuredWidth() - getPaddingLeft() - getPaddingRight()); + waveformIsLoaded = false; + waveformLoaded.set(0, true); + waveformMax.set(1, true); + } + + private static final int HANDLE_PROGRESS = 0; + private static final int HANDLE_VIDEO_SCROLL = 1; + private static final int HANDLE_VIDEO_LEFT = 2; + private static final int HANDLE_VIDEO_RIGHT = 3; + private static final int HANDLE_VIDEO_REGION = 4; + private static final int HANDLE_AUDIO_SCROLL = 5; + private static final int HANDLE_AUDIO_LEFT = 6; + private static final int HANDLE_AUDIO_RIGHT = 7; + private static final int HANDLE_AUDIO_REGION = 8; + + private int detectHandle(MotionEvent event) { + float x = event.getX(); + float y = event.getY(); + + final long scrollWidth = Math.min(getBaseDuration(), MAX_SCROLL_DURATION); + final float progressT = (Utilities.clamp(progress, getBaseDuration(), 0) + (!hasVideo ? audioOffset : 0) - scroll) / (float) scrollWidth; + final float progressX = px + ph + sw * progressT; + if (x >= progressX - dp(12) && x <= progressX + dp(12)) { + return HANDLE_PROGRESS; + } + + final boolean isInVideo = y > h - py - getVideoHeight() - dp(2); + + if (isInVideo) { + final float leftX = px + ph + (videoLeft * videoDuration - scroll) / (float) scrollWidth * sw; + final float rightX = px + ph + (videoRight * videoDuration - scroll) / (float) scrollWidth * sw; + + if (x >= leftX - dp(10 + 5) && x <= leftX + dp(5)) { + return HANDLE_VIDEO_LEFT; + } else if (x >= rightX - dp(5) && x <= rightX + dp(10 + 5)) { + return HANDLE_VIDEO_RIGHT; + } else if (x >= leftX && x <= rightX && (videoLeft > 0.01f || videoRight < .99f)) { + return HANDLE_VIDEO_REGION; + } + } else if (hasAudio) { + float leftX = px + ph + (audioOffset + audioLeft * audioDuration - scroll) / (float) scrollWidth * sw; + float rightX = px + ph + (audioOffset + audioRight * audioDuration - scroll) / (float) scrollWidth * sw; + + if (audioSelected || !hasVideo) { + if (x >= leftX - dp(10 + 5) && x <= leftX + dp(5)) { + return HANDLE_AUDIO_LEFT; + } else if (x >= rightX - dp(5) && x <= rightX + dp(10 + 5)) { + return HANDLE_AUDIO_RIGHT; + } else if (x >= leftX && x <= rightX) { + final float maxDuration = Math.min(MAX_SCROLL_DURATION, getBaseDuration()); + final float minLeft = Math.max(0, scroll - audioOffset) / (float) audioDuration; + final float maxRight = Math.min(1, Math.max(0, scroll - audioOffset + maxDuration) / (float) audioDuration); + if (audioLeft > minLeft + .01f || audioRight < maxRight - .01f) { + return HANDLE_AUDIO_REGION; + } else { + return HANDLE_AUDIO_SCROLL; + } + } + leftX = px + ph + (audioOffset - scroll) / (float) scrollWidth * sw; + rightX = px + ph + (audioOffset + audioDuration - scroll) / (float) scrollWidth * sw; + } + if (x >= leftX && x <= rightX) { + return HANDLE_AUDIO_SCROLL; + } + } + + if (videoDuration > MAX_SCROLL_DURATION && isInVideo) { + return HANDLE_VIDEO_SCROLL; + } + + return -1; + } + + public boolean onBackPressed() { + if (audioSelected) { + audioSelected = false; + return true; + } + return false; + } + + public boolean isDragging() { + return dragged; + } + + private Runnable askExactSeek; + private void setProgressAt(float x, boolean fast) { + if (!hasVideo && !hasAudio) { + return; + } + + final long scrollWidth = Math.min(getBaseDuration(), MAX_SCROLL_DURATION); + final float t = (x - px - ph) / sw; + long progress = (long) Utilities.clamp(t * scrollWidth + (!hasVideo ? -audioOffset : 0) + scroll, hasVideo ? videoDuration : audioDuration, 0); + if (hasVideo && (progress / (float) videoDuration < videoLeft || progress / (float) videoDuration > videoRight)) { + return; + } + if (hasAudio && !hasVideo && (progress / (float) audioDuration < audioLeft || progress / (float) audioDuration > audioRight)) { + return; + } + this.progress = progress; + invalidate(); + if (delegate != null) { + delegate.onProgressChange(progress, fast); + } + if (askExactSeek != null) { + AndroidUtilities.cancelRunOnUIThread(askExactSeek); + askExactSeek = null; + } + if (fast) { + AndroidUtilities.runOnUIThread(askExactSeek = () -> { + if (delegate != null) { + delegate.onProgressChange(progress, false); + } + }, 150); + } + } + + private float getVideoHeight() { + if (!hasVideo) + return 0; + float audioSelected = this.audioSelectedT.set(this.audioSelected); + return lerp(dp(28), dp(38), 1f - audioSelected); + } + + private float getAudioHeight() { + float audioSelected = this.audioSelectedT.set(this.audioSelected); + return lerp(dp(28), dp(38), audioSelected); + } + + private long lastTime; + private long pressTime; + private float lastX; + private int pressHandle = -1; + private boolean pressVideo = true; + private boolean draggingProgress, dragged; + private boolean hadDragChange; + private VelocityTracker velocityTracker; + private boolean scrollingVideo = true; + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (!hasVideo && !hasAudio) { + return false; + } + + final long now = System.currentTimeMillis(); + if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (askExactSeek != null) { + AndroidUtilities.cancelRunOnUIThread(askExactSeek); + askExactSeek = null; + } + scroller.abortAnimation(); + pressHandle = detectHandle(event); + pressVideo = !hasAudio || event.getY() > h - py - getVideoHeight() - (hasVideo ? dp(4) : 0); + pressTime = System.currentTimeMillis(); + draggingProgress = pressHandle == HANDLE_PROGRESS || pressHandle == -1 || pressHandle == HANDLE_VIDEO_SCROLL; + hadDragChange = false; + if (pressHandle == HANDLE_VIDEO_SCROLL || pressHandle == HANDLE_AUDIO_SCROLL || pressHandle == HANDLE_AUDIO_REGION) { + velocityTracker = VelocityTracker.obtain(); + } else if (velocityTracker != null) { + velocityTracker.recycle(); + velocityTracker = null; + } + dragged = false; + lastX = event.getX(); + AndroidUtilities.cancelRunOnUIThread(this.onLongPress); + AndroidUtilities.runOnUIThread(this.onLongPress, ViewConfiguration.getLongPressTimeout()); + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + final float Δx = event.getX() - lastX; + final boolean allowDrag = dragged || Math.abs(Δx) > AndroidUtilities.touchSlop; + if (allowDrag) { + final long videoScrollDuration = Math.min(getBaseDuration(), MAX_SCROLL_DURATION); + if (pressHandle == HANDLE_VIDEO_SCROLL) { + scroll = (long) Utilities.clamp(scroll - Δx / sw * videoScrollDuration, videoDuration - videoScrollDuration, 0); + invalidate(); + dragged = true; + draggingProgress = false; + } else if (pressHandle == HANDLE_VIDEO_LEFT || pressHandle == HANDLE_VIDEO_RIGHT || pressHandle == HANDLE_VIDEO_REGION) { + float d = Δx / sw * (videoScrollDuration / (float) videoDuration); + if (pressHandle == HANDLE_VIDEO_LEFT) { + videoLeft = Utilities.clamp(videoLeft + d, videoRight - MIN_SELECT_DURATION / (float) videoDuration, 0); + if (delegate != null) { + delegate.onVideoLeftChange(videoLeft); + } + if (videoRight - videoLeft > MAX_SELECT_DURATION / (float) videoDuration) { + videoRight = Math.min(1, videoLeft + MAX_SELECT_DURATION / (float) videoDuration); + if (delegate != null) { + delegate.onVideoRightChange(videoRight); + } + } + } else if (pressHandle == HANDLE_VIDEO_RIGHT) { + videoRight = Utilities.clamp(videoRight + d, 1, videoLeft + MIN_SELECT_DURATION / (float) videoDuration); + if (delegate != null) { + delegate.onVideoRightChange(videoRight); + } + if (videoRight - videoLeft > MAX_SELECT_DURATION / (float) videoDuration) { + videoLeft = Math.max(0, videoRight - MAX_SELECT_DURATION / (float) videoDuration); + if (delegate != null) { + delegate.onVideoLeftChange(videoLeft); + } + } + } else if (pressHandle == HANDLE_VIDEO_REGION) { + if (d > 0) { + d = Math.min(1 - videoRight, d); + } else { + d = Math.max(-videoLeft, d); + } + videoLeft += d; + videoRight += d; + if (delegate != null) { + delegate.onVideoLeftChange(videoLeft); + delegate.onVideoRightChange(videoRight); + } + } + if (progress / (float) videoDuration < videoLeft || progress / (float) videoDuration > videoRight) { + progress = (long) (videoLeft * videoDuration); + if (delegate != null) { + delegate.onProgressChange(progress, false); + } + } + invalidate(); + dragged = true; + draggingProgress = false; + } else if (pressHandle == HANDLE_AUDIO_LEFT || pressHandle == HANDLE_AUDIO_RIGHT || pressHandle == HANDLE_AUDIO_REGION) { + float d = Δx / sw * (videoScrollDuration / (float) audioDuration); + if (pressHandle == HANDLE_AUDIO_LEFT) { + float maxValue = audioRight - MIN_SELECT_DURATION / (float) audioDuration; + float minValue = Math.max(0, scroll - audioOffset) / (float) audioDuration; + if (!hasVideo) { + minValue = Math.max(minValue, audioRight - MAX_SELECT_DURATION / (float) audioDuration); + if (!hadDragChange && d < 0 && audioLeft <= (audioRight - MAX_SELECT_DURATION / (float) audioDuration)) { + pressHandle = HANDLE_AUDIO_REGION; + } + } + float wasAudioLeft = audioLeft; + audioLeft = Utilities.clamp(audioLeft + d, maxValue, minValue); + if (Math.abs(wasAudioLeft - audioLeft) > 0.01f) { + hadDragChange = true; + } + if (delegate != null) { + delegate.onAudioOffsetChange(audioOffset + (long) (audioLeft * audioDuration)); + } + if (delegate != null) { + delegate.onAudioLeftChange(audioLeft); + } + } else if (pressHandle == HANDLE_AUDIO_RIGHT) { + float maxValue = Math.min(1, Math.max(0, scroll - audioOffset + videoScrollDuration) / (float) audioDuration); + float minValue = audioLeft + MIN_SELECT_DURATION / (float) audioDuration; + if (!hasVideo) { + maxValue = Math.min(maxValue, audioLeft + MAX_SELECT_DURATION / (float) audioDuration); + if (!hadDragChange && d > 0 && audioRight >= (audioLeft + MAX_SELECT_DURATION / (float) audioDuration)) { + pressHandle = HANDLE_AUDIO_REGION; + } + } + float wasAudioRight = audioRight; + audioRight = Utilities.clamp(audioRight + d, maxValue, minValue); + if (Math.abs(wasAudioRight - audioRight) > 0.01f) { + hadDragChange = true; + } + if (delegate != null) { + delegate.onAudioRightChange(audioRight); + } + } + if (pressHandle == HANDLE_AUDIO_REGION) { + float minLeft = Math.max(0, scroll - audioOffset) / (float) audioDuration; + float maxRight = Math.min(1, Math.max(0, scroll - audioOffset + videoScrollDuration) / (float) audioDuration); + if (d > 0) { + d = Math.min(maxRight - audioRight, d); + } else { + d = Math.max(minLeft - audioLeft, d); + } + audioLeft += d; + audioRight += d; + + if (delegate != null) { + delegate.onAudioLeftChange(audioLeft); + delegate.onAudioOffsetChange(audioOffset + (long) (audioLeft * audioDuration)); + delegate.onAudioRightChange(audioRight); + } + } + if (!hasVideo && (progress / (float) audioDuration < audioLeft || progress / (float) audioDuration > audioRight)) { + progress = (long) (audioLeft * audioDuration); + if (delegate != null) { + delegate.onProgressChange(progress, false); + } + } + invalidate(); + dragged = true; + draggingProgress = false; + } else if (pressHandle == HANDLE_AUDIO_SCROLL) { + float d = Δx / sw * videoScrollDuration; + if (!hasVideo) { + audioOffset = Utilities.clamp(audioOffset + (long) d, 0, (long) -(audioDuration - Math.min(getBaseDuration(), MAX_SCROLL_DURATION))); + } else if (audioSelected) { + final long mx = (long) Math.max(getBaseDuration(), audioDuration); + final long mn = (long) Math.min(getBaseDuration(), audioDuration); + audioOffset = Utilities.clamp(audioOffset + (long) d, (long) (getBaseDuration() - audioDuration * audioRight), mn - mx); + } else { + audioOffset = Utilities.clamp(audioOffset + (long) d, (long) (getBaseDuration() - audioDuration * audioRight), (long) (-audioLeft * audioDuration)); + } + final float minLeft = Math.max(0, scroll - audioOffset) / (float) audioDuration; + final float maxRight = Math.min(1, Math.max(0, scroll - audioOffset + videoScrollDuration) / (float) audioDuration); + boolean changedLeftRight = false; + final float pastDuration = audioRight - audioLeft; + if (audioLeft < minLeft) { + audioLeft = minLeft; + audioRight = Math.min(1, audioLeft + pastDuration); + changedLeftRight = true; + } + if (audioRight > maxRight) { + audioRight = maxRight; + audioLeft = Math.max(0, audioRight - pastDuration); + changedLeftRight = true; + } + if (delegate != null && changedLeftRight) { + delegate.onAudioLeftChange(audioLeft); + delegate.onAudioRightChange(audioRight); + } + if (!hasVideo && (progress / (float) audioDuration < audioLeft || progress / (float) audioDuration > audioRight)) { + progress = (long) (audioLeft * audioDuration); + if (delegate != null) { + delegate.onProgressChange(progress, false); + } + } + invalidate(); + if (delegate != null) { + delegate.onAudioOffsetChange(audioOffset + (long) (audioLeft * audioDuration)); + } + if (!dragged && delegate != null) { + delegate.onProgressDragChange(true); + } + dragged = true; + draggingProgress = false; + } else if (draggingProgress) { + setProgressAt(event.getX(), now - lastTime < 350); + if (!dragged && delegate != null) { + delegate.onProgressDragChange(true); + } + dragged = true; + } + lastX = event.getX(); + } + if (dragged) { + AndroidUtilities.cancelRunOnUIThread(this.onLongPress); + } + if ((pressHandle == HANDLE_VIDEO_SCROLL || pressHandle == HANDLE_AUDIO_SCROLL || pressHandle == HANDLE_AUDIO_REGION) && velocityTracker != null) { + velocityTracker.addMovement(event); + } + } else if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) { + AndroidUtilities.cancelRunOnUIThread(this.onLongPress); + scroller.abortAnimation(); + if (event.getAction() == MotionEvent.ACTION_UP) { + if (System.currentTimeMillis() - pressTime <= ViewConfiguration.getTapTimeout() && !dragged) { + if (!pressVideo && !audioSelected) { + audioSelected = true; + invalidate(); + } else if (pressVideo && audioSelected) { + audioSelected = false; + invalidate(); + } else { + setProgressAt(event.getX(), false); + } + } else if (pressHandle == HANDLE_VIDEO_SCROLL && velocityTracker != null) { + velocityTracker.computeCurrentVelocity(1000); + final int velocity = (int) velocityTracker.getXVelocity(); + scrollingVideo = true; + if (Math.abs(velocity) > dp(100)) { + final long videoScrollDuration = Math.min(videoDuration, MAX_SCROLL_DURATION); + final int scrollX = (int) (px + scroll / (float) videoScrollDuration * sw); + final int maxScrollX = (int) (px + (videoDuration - videoScrollDuration) / (float) videoScrollDuration * sw); + scroller.fling(scrollX, 0, -velocity, 0, px, maxScrollX, 0, 0); + } + } else if ((pressHandle == HANDLE_AUDIO_SCROLL || pressHandle == HANDLE_AUDIO_REGION && !dragged) && hasVideo && audioSelected && velocityTracker != null) { + velocityTracker.computeCurrentVelocity(1000); + final int velocity = (int) velocityTracker.getXVelocity(); + scrollingVideo = false; + if (Math.abs(velocity) > dp(100)) { + final long videoScrollDuration = Math.min(getBaseDuration(), MAX_SCROLL_DURATION); + final int scrollX = (int) (px + audioOffset / (float) videoScrollDuration * sw); + final long mx = (long) Math.max(getBaseDuration(), audioDuration); + final long mn = (long) Math.min(getBaseDuration(), audioDuration); + scroller.fling(scrollX, 0, velocity, 0, (int) (px + ph + (mn - mx) / (float) videoScrollDuration * sw), px + ph, 0, 0); + } + } + } + if (askExactSeek != null) { + AndroidUtilities.cancelRunOnUIThread(askExactSeek); + askExactSeek = null; + } + if (dragged && delegate != null) { + delegate.onProgressDragChange(false); + } + dragged = false; + draggingProgress = false; + pressTime = -1; + pressHandle = -1; + if (velocityTracker != null) { + velocityTracker.recycle(); + velocityTracker = null; + } + } + lastTime = System.currentTimeMillis(); + return true; + } + + @Override + public void computeScroll() { + if (scroller.computeScrollOffset()) { + float scrollX = scroller.getCurrX(); + final long videoScrollDuration = Math.min(getBaseDuration(), MAX_SCROLL_DURATION); + if (scrollingVideo) { + long wasScroll = scroll; + scroll = (long) Math.max(0, ((scrollX - px - ph) / (float) sw * videoScrollDuration)); +// videoLeft = Utilities.clamp(videoLeft + (scroll - wasScroll) / (float) videoDuration, 1, 0); +// videoRight = Utilities.clamp(videoRight + (scroll - wasScroll) / (float) videoDuration, 1, 0); +// if (delegate != null) { +// delegate.onVideoLeftChange(videoLeft); +// delegate.onVideoRightChange(videoRight); +// } + } else { + audioOffset = (long) ((scrollX - px - ph) / (float) sw * videoScrollDuration); + final float minLeft = Math.max(0, scroll - audioOffset) / (float) audioDuration; + final float maxRight = Math.min(1, Math.max(0, scroll - audioOffset + videoScrollDuration) / (float) audioDuration); + boolean changedLeftRight = false; + final float pastDuration = audioRight - audioLeft; + if (audioLeft < minLeft) { + audioLeft = minLeft; + audioRight = Math.min(1, audioLeft + pastDuration); + changedLeftRight = true; + } + if (audioRight > maxRight) { + audioRight = maxRight; + audioLeft = Math.max(0, audioRight - pastDuration); + changedLeftRight = true; + } + if (delegate != null && changedLeftRight) { + delegate.onAudioLeftChange(audioLeft); + delegate.onAudioRightChange(audioRight); + } + } + invalidate(); + } + } + + final float[] selectedVideoRadii = new float[8]; + final float[] waveformRadii = new float[8]; + + @Override + protected void dispatchDraw(Canvas canvas) { + final Paint blurPaint = backgroundBlur.getPaint(1f); + + final long scrollDuration = Math.min(getBaseDuration(), MAX_SCROLL_DURATION); + float videoHeight = 0; + float videoT = hasVideo ? 1 : 0; + + // draw video thumbs + if (hasVideo) { + canvas.save(); + videoHeight = getVideoHeight(); + final float videoStartX = (videoDuration <= 0 ? 0 : px + ph - scroll / (float) scrollDuration * sw) - ph; + final float videoEndX = (videoDuration <= 0 ? 0 : px + ph + (videoDuration - scroll) / (float) scrollDuration * sw) + ph; + videoBounds.set(videoStartX, h - py - videoHeight, videoEndX, h - py); + videoClipPath.rewind(); + videoClipPath.addRoundRect(videoBounds, dp(8), dp(8), Path.Direction.CW); + canvas.clipPath(videoClipPath); + if (thumbs != null) { + float x = videoStartX; + final int frameWidth = thumbs.getFrameWidth(); + final int fromFrame = (int) Math.max(0, Math.floor((videoStartX - px) / frameWidth)); + final int toFrame = (int) Math.min(thumbs.count, Math.ceil(((videoEndX - videoStartX) - px) / frameWidth) + 1); + + final int y = (int) (h - py - videoHeight); + + boolean allLoaded = thumbs.frames.size() >= toFrame; + boolean fullyCovered = allLoaded; + if (fullyCovered) { + for (int i = fromFrame; i < Math.min(thumbs.frames.size(), toFrame); ++i) { + VideoThumbsLoader.BitmapFrame frame = thumbs.frames.get(i); + if (frame.bitmap == null) { + fullyCovered = false; + break; + } + } + } + + if (!fullyCovered) { + if (blurPaint == null) { + canvas.drawColor(0x40000000); + } else { + canvas.drawRect(videoBounds, blurPaint); + canvas.drawColor(0x33000000); + } + } + + for (int i = fromFrame; i < Math.min(thumbs.frames.size(), toFrame); ++i) { + VideoThumbsLoader.BitmapFrame frame = thumbs.frames.get(i); + if (frame.bitmap != null) { + videoFramePaint.setAlpha((int) (0xFF * frame.getAlpha())); + canvas.drawBitmap(frame.bitmap, x, y - (int) ((frame.bitmap.getHeight() - videoHeight) / 2f), videoFramePaint); + } + x += frameWidth; + } + + if (!allLoaded) { + thumbs.load(); + } + } + selectedVideoClipPath.rewind(); + + AndroidUtilities.rectTmp.set( + px + ph + (videoLeft * videoDuration - scroll) / (float) scrollDuration * sw - (videoLeft <= 0 ? ph : 0), + h - py - videoHeight, + px + ph + (videoRight * videoDuration - scroll) / (float) scrollDuration * sw + (videoRight >= 1 ? ph : 0), + h - py + ); + selectedVideoClipPath.addRoundRect( + AndroidUtilities.rectTmp, + selectedVideoRadii, + Path.Direction.CW + ); + canvas.clipPath(selectedVideoClipPath, Region.Op.DIFFERENCE); + canvas.drawColor(0x80000000); + canvas.restore(); + } + + // draw audio + float audioT = this.audioT.set(hasAudio); + float audioSelected = this.audioSelectedT.set(hasAudio && this.audioSelected); + final float p = dp(4); + final float audioHeight = getAudioHeight() * audioT; + if (audioT > 0) { + final Paint audioBlurPaint = audioBlur.getPaint(audioT); + canvas.save(); + float left, right; + if (hasVideo) { + left = px + ph + (audioOffset - scroll + lerp(audioLeft, 0, audioSelected) * audioDuration) / (float) scrollDuration * sw; + right = px + ph + (audioOffset - scroll + lerp(audioRight, 1, audioSelected) * audioDuration) / (float) scrollDuration * sw; + } else { + left = px + ph + (audioOffset - scroll) / (float) scrollDuration * sw; + right = px + ph + (audioOffset - scroll + audioDuration) / (float) scrollDuration * sw; + } + + final float bottom = h - py - videoHeight - p * videoT; + audioBounds.set(left - ph, bottom - audioHeight, right + ph, bottom); + audioClipPath.rewind(); + audioClipPath.addRoundRect(audioBounds, dp(8), dp(8), Path.Direction.CW); + canvas.clipPath(audioClipPath); + + if (audioBlurPaint == null) { + canvas.drawColor(Theme.multAlpha(0x40000000, audioT)); + } else { + canvas.drawRect(audioBounds, audioBlurPaint); + canvas.drawColor(Theme.multAlpha(0x33000000, audioT)); + } + + if (waveform != null && audioBlurPaint != null) { + Paint paint = audioWaveformBlur.getPaint(.4f * audioT); + if (paint == null) { + paint = waveformPaint; + paint.setAlpha((int) (0x40 * audioT)); + } + final float maxBar = waveformMax.set(waveform.getMaxBar(), !waveformIsLoaded); + waveformIsLoaded = waveform.getLoadedCount() > 0; + final float animatedLoaded = waveformLoaded.set(waveform.getLoadedCount()); + final float barWidth = Math.round(dpf2(3.3333f)); + waveformPath.rewind(); + final float start = px + ph + (audioOffset - scroll) / (float) scrollDuration * sw; + int from = Math.max(0, (int) ((left - ph - start) / barWidth)); + int to = Math.min(waveform.getCount() - 1, (int) Math.ceil((right + ph - start) / barWidth)); + for (int i = from; i <= to; ++i) { + float x = start + i * barWidth + dp(2); + float h = maxBar <= 0 ? 0 : waveform.getBar(i) / (float) maxBar * audioHeight * .6f; + if (i < animatedLoaded && i + 1 > animatedLoaded) { + h *= (animatedLoaded - i); + } else if (i > animatedLoaded) { + h = 0; + } + if (x < left || x > right) { + h *= audioSelected; + if (h <= 0) { + continue; + } + } + h = Math.max(h, lerp(dpf2(0.66f), dpf2(1.5f), audioSelected)); + AndroidUtilities.rectTmp.set( + x, + lerp(bottom - h, bottom - (audioHeight + h) / 2f, audioSelected), + x + dpf2(1.66f), + lerp(bottom, bottom - (audioHeight - h) / 2f, audioSelected) + ); + waveformPath.addRoundRect(AndroidUtilities.rectTmp, waveformRadii, Path.Direction.CW); + } + canvas.drawPath(waveformPath, paint); + } + + if (audioSelected < 1) { + final float tleft = px + ph + (audioOffset - scroll + audioLeft * audioDuration) / (float) scrollDuration * sw; + final float tright = px + ph + (audioOffset - scroll + audioRight * audioDuration) / (float) scrollDuration * sw; + + final float textCx = (Math.max(px, tleft) + Math.min(w - px, tright)) / 2f; + final float textCy = bottom - audioHeight + dp(28 / 2); + final float textMaxWidth = Math.max(0, Math.min(w - px, tright) - Math.max(px, tleft) - dp(24)); + float textWidth = dpf2(13) + (audioAuthor == null && audioTitle == null ? 0 : dpf2(3.11f) + audioAuthorWidth + dpf2(3.66f + 2 + 4) + audioTitleWidth); + final boolean fit = textWidth < textMaxWidth; + + float x = textCx - Math.min(textWidth, textMaxWidth) / 2f; + audioIcon.setBounds((int) x, (int) (textCy - dp(13) / 2f), (int) (x + dp(13)), (int) (textCy + dp(13) / 2f)); + audioIcon.setAlpha((int) (0xFF * (1f - audioSelected))); + audioIcon.draw(canvas); + x += dpf2(13 + 3.11f); + canvas.saveLayerAlpha(0, 0, w, h, 0xFF, Canvas.ALL_SAVE_FLAG); + final float x2 = Math.min(tright, w) - dp(12); + canvas.clipRect(x, 0, x2, h); + if (audioAuthor != null) { + canvas.save(); + canvas.translate(x - audioAuthorLeft, textCy - audioAuthor.getHeight() / 2f); + audioAuthorPaint.setAlpha((int) (0xFF * (1f - audioSelected) * audioT)); + audioAuthor.draw(canvas); + canvas.restore(); + x += audioAuthorWidth; + } + if (audioAuthor != null && audioTitle != null) { + x += dpf2(3.66f); + int wasAlpha = audioDotPaint.getAlpha(); + audioDotPaint.setAlpha((int) (wasAlpha * (1f - audioSelected))); + canvas.drawCircle(x + dp(1), textCy, dp(1), audioDotPaint); + audioDotPaint.setAlpha(wasAlpha); + x += dpf2(2); + x += dpf2(4); + } + if (audioTitle != null) { + canvas.save(); + canvas.translate(x - audioTitleLeft, textCy - audioTitle.getHeight() / 2f); + audioTitlePaint.setAlpha((int) (0xFF * (1f - audioSelected) * audioT)); + audioTitle.draw(canvas); + canvas.restore(); + } + if (!fit) { + ellipsizeMatrix.reset(); + ellipsizeMatrix.postScale(dpf2(8) / 16, 1); + ellipsizeMatrix.postTranslate(x2 - dp(8), 0); + ellipsizeGradient.setLocalMatrix(ellipsizeMatrix); + canvas.drawRect(x2 - dp(8), bottom - audioHeight, x2, bottom, ellipsizePaint); + } + canvas.restore(); + } + canvas.restore(); + } + + // draw region + final float regionTop = lerp(h - py - videoHeight, h - py - videoHeight - p * videoT - audioHeight, hasVideo ? audioSelected : 1); + final float regionBottom = lerp(h - py, h - py - videoHeight - p * videoT, audioSelected); + final float left = lerp(videoLeft * videoDuration, audioOffset + audioLeft * audioDuration, hasVideo ? audioSelected : 1); + final float right = lerp(videoRight * videoDuration, audioOffset + audioRight * audioDuration, hasVideo ? audioSelected : 1); + float leftX = px + ph + (left - scroll) / (float) scrollDuration * sw; + float rightX = px + ph + (right - scroll) / (float) scrollDuration * sw; + if (audioT > 0. || videoT > 0.) { + drawRegion(canvas, blurPaint, regionTop, regionBottom, leftX, rightX, hasVideo ? 1 : lerp(.6f, 1f, audioSelected) * audioT); + + // draw progress + float loopT = loopProgress.set(0); + final float y1 = h - py - videoHeight - (audioHeight + p * videoT) * audioT - dpf2(4.3f); + final float y2 = h - py + dpf2(4.3f); + if (loopT > 0) { + drawProgress(canvas, y1, y2, (long) (hasVideo ? videoDuration * videoRight : audioDuration * audioRight), loopT); + } + drawProgress(canvas, y1, y2, progress, 1f - loopT); + } + + if (dragged) { + long Δd = (long) (dp(86) / (float) sw * scrollDuration * (1f / (1000f / AndroidUtilities.screenRefreshRate))); + if (pressHandle == HANDLE_VIDEO_REGION) { + int direction = 0; + if (videoLeft < (scroll / (float) videoDuration)) { + direction = -1; + } else if (videoRight > ((scroll + scrollDuration) / (float) videoDuration)) { + direction = +1; + } + long wasScroll = scroll; + scroll = Utilities.clamp(scroll + direction * Δd, videoDuration - scrollDuration, 0); + progress += direction * Δd; + float d = (scroll - wasScroll) / (float) videoDuration; + if (d > 0) { + d = Math.min(1 - videoRight, d); + } else { + d = Math.max(0 - videoLeft, d); + } + videoLeft = Utilities.clamp(videoLeft + d, 1, 0); + videoRight = Utilities.clamp(videoRight + d, 1, 0); + if (delegate != null) { + delegate.onVideoLeftChange(videoLeft); + delegate.onVideoRightChange(videoRight); + } + invalidate(); + } else if (!hasVideo && pressHandle == HANDLE_AUDIO_REGION) { + int direction = 0; + if (audioLeft < ((-audioOffset + 100) / (float) audioDuration)) { + direction = -1; + } else if (audioRight >= ((-audioOffset + scrollDuration - 100) / (float) audioDuration)) { + direction = +1; + } + long wasOffset = audioOffset; + audioOffset = Utilities.clamp(audioOffset - direction * Δd, 0, (long) -(audioDuration - Math.min(getBaseDuration(), MAX_SCROLL_DURATION))); + float d = -(audioOffset - wasOffset) / (float) audioDuration; + if (d > 0) { + d = Math.min(1 - audioRight, d); + } else { + d = Math.max(0 - audioLeft, d); + } + progress = (long) Utilities.clamp(progress + d * audioDuration, audioDuration, 0); + audioLeft = Utilities.clamp(audioLeft + d, 1, 0); + audioRight = Utilities.clamp(audioRight + d, 1, 0); + if (delegate != null) { + delegate.onAudioLeftChange(audioLeft); + delegate.onAudioRightChange(audioRight); + delegate.onProgressChange(progress, false); + } + invalidate(); + } + } + } + + private void drawRegion(Canvas canvas, Paint blurPaint, float top, float bottom, float left, float right, float alpha) { + AndroidUtilities.rectTmp.set(left - dp(10), top, right + dp(10), bottom); + canvas.saveLayerAlpha(0, 0, w, h, 0xFF, Canvas.ALL_SAVE_FLAG); + regionPaint.setAlpha((int) (0xFF * alpha)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(6), dp(6), regionPaint); + AndroidUtilities.rectTmp.inset(dp(10), dp(2)); + canvas.drawRect(AndroidUtilities.rectTmp, regionCutPaint); + + final float hw = dp(2), hh = dp(10); + Paint handlePaint = blurPaint != null ? blurPaint : regionHandlePaint; + handlePaint.setAlpha((int) (0xFF * alpha)); + AndroidUtilities.rectTmp.set( + left - (dp(10) - hw) / 2f, + (top + bottom - hh) / 2f, + left - (dp(10) + hw) / 2f, + (top + bottom + hh) / 2f + ); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(1), dp(1), handlePaint); + AndroidUtilities.rectTmp.set( + right + (dp(10) - hw) / 2f, + (top + bottom - hh) / 2f, + right + (dp(10) + hw) / 2f, + (top + bottom + hh) / 2f + ); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(1), dp(1), handlePaint); + + canvas.restore(); + } + + private void drawProgress(Canvas canvas, float y1, float y2, long progress, float scale) { + final long scrollDuration = Math.min(getBaseDuration(), MAX_SCROLL_DURATION); + + final float progressT = (Utilities.clamp(progress, getBaseDuration(), 0) + (!hasVideo ? audioOffset : 0) - scroll) / (float) scrollDuration; + final float progressX = px + ph + sw * progressT; + + float yd = (y1 + y2) / 2; + y1 += yd / 2f * (1f - scale); + y2 -= yd / 2f * (1f - scale); + progressShadowPaint.setAlpha((int) (0x26 * scale)); + progressWhitePaint.setAlpha((int) (0xFF * scale)); + + AndroidUtilities.rectTmp.set(progressX - dpf2(1.5f), y1, progressX + dpf2(1.5f), y2); + AndroidUtilities.rectTmp.inset(-dpf2(0.66f), -dpf2(0.66f)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(6), dp(6), progressShadowPaint); + AndroidUtilities.rectTmp.set(progressX - dpf2(1.5f), y1, progressX + dpf2(1.5f), y2); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(6), dp(6), progressWhitePaint); + } + + private int sw; + private int w, h, ph, px, py; + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + audioAuthorPaint.setTextSize(dp(12)); + audioTitlePaint.setTextSize(dp(12)); + waveformRadii[0] = waveformRadii[1] = waveformRadii[2] = waveformRadii[3] = dp(2); + waveformRadii[4] = waveformRadii[5] = waveformRadii[6] = waveformRadii[7] = 0; + setPadding(px = dp(12), py = dp(5), dp(12), dp(5)); + setMeasuredDimension(w = MeasureSpec.getSize(widthMeasureSpec), h = dp(80)); + ph = dp(10); + sw = w - 2 * ph - 2 * px; + if (videoPath != null && this.thumbs == null) { + setupVideoThumbs(); + } + if (audioPath != null && this.waveform == null) { + setupAudioWaveform(); + } + } + + private class VideoThumbsLoader { + + private long duration; + private final long frameIterator; + private final int count; + + private final ArrayList frames = new ArrayList<>(); + private MediaMetadataRetriever metadataRetriever; + + private final int frameWidth; + private final int frameHeight; + + private boolean destroyed; + + public VideoThumbsLoader(String path, int uiWidth, int uiHeight) { + metadataRetriever = new MediaMetadataRetriever(); + long duration = MAX_SCROLL_DURATION; + int width = 0; + int height = 0; + try { + metadataRetriever.setDataSource(path); + String value; + value = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); + if (value != null) { + this.duration = duration = Long.parseLong(value); + } + value = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH); + if (value != null) { + width = Integer.parseInt(value); + } + value = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT); + if (value != null) { + height = Integer.parseInt(value); + } + value = metadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION); + if (value != null) { + int orientation = Integer.parseInt(value); + if (orientation == 90 || orientation == 270) { + int temp = width; + width = height; + height = temp; + } + } + } catch (Exception e) { + metadataRetriever = null; + FileLog.e(e); + } + float aspectRatio = 1; + if (width != 0 && height != 0) { + aspectRatio = width / (float) height; + } + aspectRatio = Utilities.clamp(aspectRatio, 4 / 3f, 9f / 16f); + frameHeight = Math.max(1, uiHeight); + frameWidth = Math.max(1, (int) Math.ceil(uiHeight * aspectRatio)); + final float uiScrollWidth = Math.max(duration, MAX_SCROLL_DURATION) / (float) MAX_SCROLL_DURATION * uiWidth; + count = (int) Math.ceil(uiScrollWidth / frameWidth); + frameIterator = (long) (duration / (float) count); + nextFrame = -frameIterator; + load(); + } + + public int getFrameWidth() { + return frameWidth; + } + + public long getDuration() { + return duration; + } + + public BitmapFrame getFrameAt(int index) { + if (index < 0 || index >= frames.size()) { + return null; + } + return frames.get(index); + } + + public int getLoadedCount() { + return frames.size(); + } + + public int getCount() { + return count; + } + + private long nextFrame; + private boolean loading = false; + + // (ui) load() -> (themeQueue) retrieveFrame() -> (ui) receiveFrame() + public void load() { + if (loading || metadataRetriever == null || frames.size() >= count) { + return; + } + loading = true; + nextFrame += frameIterator; + Utilities.themeQueue.cancelRunnable(this::retrieveFrame); + Utilities.themeQueue.postRunnable(this::retrieveFrame); + } + + private final Paint bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + + private void retrieveFrame() { + if (metadataRetriever == null) { + return; + } + + Bitmap bitmap = null; + try { + bitmap = metadataRetriever.getFrameAtTime(nextFrame * 1000, MediaMetadataRetriever.OPTION_CLOSEST_SYNC); + if (bitmap != null) { + Bitmap scaledBitmap = Bitmap.createBitmap(frameWidth, frameHeight, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(scaledBitmap); + float scale = Math.max((float) frameWidth / bitmap.getWidth(), (float) frameHeight / bitmap.getHeight()); + Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); + Rect dest = new Rect( + (int) ((scaledBitmap.getWidth() - bitmap.getWidth() * scale) / 2f), + (int) ((scaledBitmap.getHeight() - bitmap.getHeight() * scale) / 2f), + (int) ((scaledBitmap.getWidth() + bitmap.getWidth() * scale) / 2f), + (int) ((scaledBitmap.getHeight() + bitmap.getHeight() * scale) / 2f) + ); + canvas.drawBitmap(bitmap, src, dest, bitmapPaint); + bitmap.recycle(); + bitmap = scaledBitmap; + } + } catch (Exception e) { + FileLog.e(e); + } + + final Bitmap finalBitmap = bitmap; + AndroidUtilities.runOnUIThread(() -> receiveFrame(finalBitmap)); + }; + + private void receiveFrame(Bitmap bitmap) { + if (!loading || destroyed) { + return; + } + frames.add(new BitmapFrame(bitmap)); + loading = false; + invalidate(); + } + + public void destroy() { + destroyed = true; + Utilities.themeQueue.cancelRunnable(this::retrieveFrame); + for (BitmapFrame frame : frames) { + if (frame.bitmap != null) { + frame.bitmap.recycle(); + } + } + frames.clear(); + if (metadataRetriever != null) { + try { + metadataRetriever.release(); + } catch (Exception e) { + metadataRetriever = null; + FileLog.e(e); + } + } + } + + public class BitmapFrame { + public Bitmap bitmap; + private final AnimatedFloat alpha = new AnimatedFloat(0, TimelineView.this, 0, 240, CubicBezierInterpolator.EASE_OUT_QUINT); + + public BitmapFrame(Bitmap bitmap) { + this.bitmap = bitmap; + } + + public float getAlpha() { + return alpha.set(1); + } + } + } + + private class AudioWaveformLoader { + + private final int count; + private int loaded = 0; + private final short[] data; + private short max; + + private final MediaExtractor extractor; + private MediaFormat inputFormat; + private long duration; + + private final Object lock = new Object(); + private boolean stop = false; + + private FfmpegAudioWaveformLoader waveformLoader; + + public AudioWaveformLoader(String path, int uiWidth) { + + extractor = new MediaExtractor(); + String mime = null; + try { + extractor.setDataSource(path); + + int numTracks = extractor.getTrackCount(); + for (int i = 0; i < numTracks; ++i) { + MediaFormat format = extractor.getTrackFormat(i); + mime = format.getString(MediaFormat.KEY_MIME); + if (mime != null && mime.startsWith("audio/")) { + extractor.selectTrack(i); + inputFormat = format; + break; + } + } + + if (inputFormat != null) { + duration = inputFormat.getLong(MediaFormat.KEY_DURATION) / 1_000_000L; + } + } catch (Exception e) { + FileLog.e(e); + } + + final float videoScrollWidth = Math.min(hasVideo ? videoDuration : duration * 1000, MAX_SCROLL_DURATION); + final float uiScrollWidth = (duration * 1000) / videoScrollWidth * uiWidth; + final int sampleWidth = Math.round(dpf2(3.3333f)); + count = Math.round(uiScrollWidth / sampleWidth); + data = new short[count]; + + if (duration > 0 && inputFormat != null) { + if ("audio/mpeg".equals(mime) || "audio/mp3".equals(mime)) { + waveformLoader = new FfmpegAudioWaveformLoader(path, count, this::receiveData); + } else { + Utilities.phoneBookQueue.postRunnable(this::run); + } + } + } + + private void run() { + try { + final int sampleRate = inputFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE); + final int skip = 4; + final int barWidth = (int) Math.round(duration * sampleRate / (float) count / (1 + skip)); + final long chunkTime = 2500; + String mime = inputFormat.getString(MediaFormat.KEY_MIME); + final MediaCodec decoder = MediaCodec.createDecoderByType(mime); + if (decoder == null) { + return; + } + decoder.configure(inputFormat, null, null, 0); + decoder.start(); + + ByteBuffer[] inputBuffers = decoder.getInputBuffers(); + ByteBuffer[] outputBuffers = decoder.getOutputBuffers(); + int outputBufferIndex = -1; + + int chunkindex = 0; + short[] chunk = new short[32]; + + int index = 0, count = 0; + short peak = 0; + boolean end = false; + + do { + MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); + int inputBufferIndex = decoder.dequeueInputBuffer(chunkTime); + if (inputBufferIndex >= 0) { + ByteBuffer inputBuffer; + if (Build.VERSION.SDK_INT < 21) { + inputBuffer = inputBuffers[inputBufferIndex]; + } else { + inputBuffer = decoder.getInputBuffer(inputBufferIndex); + } + int size = extractor.readSampleData(inputBuffer, 0); + if (size < 0) { + decoder.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); + end = true; + } else { + decoder.queueInputBuffer(inputBufferIndex, 0, size, extractor.getSampleTime(), 0); + extractor.advance(); + } + } + + if (outputBufferIndex >= 0) { + ByteBuffer outputBuffer; + if (Build.VERSION.SDK_INT < 21) { + outputBuffer = outputBuffers[outputBufferIndex]; + } else { + outputBuffer = decoder.getOutputBuffer(outputBufferIndex); + } + // Ensure that the data is placed at the start of the buffer + outputBuffer.position(0); + } + + outputBufferIndex = decoder.dequeueOutputBuffer(info, chunkTime); + while (outputBufferIndex != MediaCodec.INFO_TRY_AGAIN_LATER && !end) { + if (outputBufferIndex >= 0) { + ByteBuffer outputBuffer; + if (Build.VERSION.SDK_INT < 21) { + outputBuffer = outputBuffers[outputBufferIndex]; + } else { + outputBuffer = decoder.getOutputBuffer(outputBufferIndex); + } + if (outputBuffer != null && info.size > 0) { + while (outputBuffer.remaining() > 0) { + byte a = outputBuffer.get(); + byte b = outputBuffer.get(); + + short value = (short) (((b & 0xFF) << 8) | (a & 0xFF)); + + if (count >= barWidth) { + chunk[index - chunkindex] = peak; + index++; + if (index - chunkindex >= chunk.length || index >= this.count) { + short[] dataToSend = chunk; + int length = index - chunkindex; + chunk = new short[chunk.length]; + chunkindex = index; + AndroidUtilities.runOnUIThread(() -> receiveData(dataToSend, length)); + } + peak = 0; + count = 0; + if (index >= data.length) { + break; + } + } + + if (peak < value) { + peak = value; + } + count++; + + if (outputBuffer.remaining() < skip * 2) + break; + outputBuffer.position(outputBuffer.position() + skip * 2); + } + } + decoder.releaseOutputBuffer(outputBufferIndex, false); + + if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { + end = true; + break; + } + + } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { + outputBuffers = decoder.getOutputBuffers(); + } + outputBufferIndex = decoder.dequeueOutputBuffer(info, chunkTime); + } + synchronized (lock) { + if (stop) { + break; + } + } + } while (!end && index < this.count); + + decoder.stop(); + decoder.release(); + extractor.release(); + } catch (Exception e) { + FileLog.e(e); + } + } + + private void receiveData(short[] data, int len) { + for (int i = 0; i < len; ++i) { + if (loaded + i >= this.data.length) { + break; + } + this.data[loaded + i] = data[i]; + if (max < data[i]) { + max = data[i]; + } + } + loaded += len; + invalidate(); + } + + public void destroy() { + if (waveformLoader != null) { + waveformLoader.destroy(); + } + Utilities.phoneBookQueue.cancelRunnable(this::run); + synchronized (lock) { + stop = true; + } + } + + public short getMaxBar() { + return max; + } + + public short getBar(int index) { + return data[index]; + } + + public int getLoadedCount() { + return loaded; + } + + public int getCount() { + return count; + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/VolumeSliderView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/VolumeSliderView.java new file mode 100644 index 0000000000..ac60c2d4de --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/VolumeSliderView.java @@ -0,0 +1,220 @@ +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.CornerPathEffect; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.drawable.Drawable; +import android.text.TextPaint; +import android.text.TextUtils; +import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; +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.messenger.Utilities; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.CubicBezierInterpolator; + +public class VolumeSliderView extends View { + + private float minVolume = 0; + private float maxVolume = 1.5f; + private float value; + private Utilities.Callback onValueChange; + + private final Paint whitePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint speaker1Paint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint speaker2Paint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint speakerWave1Paint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint speakerWave2Paint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private final AnimatedTextView.AnimatedTextDrawable text = new AnimatedTextView.AnimatedTextDrawable(false, true, true); + + public VolumeSliderView(Context context) { + super(context); + + text.setTextSize(dp(15)); + text.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + text.setAnimationProperties(.3f, 0, 40, CubicBezierInterpolator.EASE_OUT_QUINT); + text.setCallback(this); + text.setTextColor(0xffffffff); + text.setText(""); + + speaker1Paint.setColor(0xffffffff); + speaker2Paint.setColor(0xffffffff); + speakerWave1Paint.setColor(0xffffffff); + speakerWave2Paint.setColor(0xffffffff); + speakerWave2Paint.setStyle(Paint.Style.STROKE); + speakerWave2Paint.setStrokeCap(Paint.Cap.ROUND); + + whitePaint.setColor(0xffffffff); + whitePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR)); + } + + public VolumeSliderView setMinMax(float min, float max) { + this.minVolume = min; + this.maxVolume = max; + return this; + } + + public VolumeSliderView setVolume(float volume) { + this.value = (volume - this.minVolume) / (this.maxVolume - this.minVolume); + updateText(volume); + return this; + } + + public VolumeSliderView setOnValueChange(Utilities.Callback listener) { + onValueChange = listener; + return this; + } + + private final Path clipPath = new Path(); + private final Path speaker1Path = new Path(); + private final Path speaker2Path = new Path(); + private final Path speakerWave1Path = new Path(); + private final Path speakerWave2Path = new Path(); + + private final AnimatedFloat wave1Alpha = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat wave2Alpha = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + + canvas.save(); + AndroidUtilities.rectTmp.set(0, 0, w, h); + clipPath.addRoundRect(AndroidUtilities.rectTmp, r, r, Path.Direction.CW); + canvas.clipPath(clipPath); + + canvas.saveLayerAlpha(0, 0, w, h, 0xFF, Canvas.ALL_SAVE_FLAG); + + text.setBounds(dp(42), -dp(1), w, h - dp(1)); + text.draw(canvas); + + canvas.drawPath(speaker1Path, speaker1Paint); + canvas.drawPath(speaker2Path, speaker2Paint); + + final float volume = this.maxVolume - this.minVolume != 0 ? this.minVolume + this.value * (this.maxVolume - this.minVolume) : 0; + final float wave1Alpha = this.wave1Alpha.set(volume > .25); + canvas.save(); + canvas.translate(-dpf2(0.33f) * (1f - wave1Alpha), 0); + speakerWave1Paint.setAlpha((int) (0xFF * wave1Alpha)); + canvas.drawPath(speakerWave1Path, speakerWave1Paint); + canvas.restore(); + + final float wave2Alpha = this.wave2Alpha.set(volume > .5); + canvas.save(); + canvas.translate(-dpf2(0.66f) * (1f - wave2Alpha), 0); + speakerWave2Paint.setAlpha((int) (0xFF * wave2Alpha)); + canvas.drawPath(speakerWave2Path, speakerWave2Paint); + canvas.restore(); + + canvas.save(); + canvas.drawRect(0, 0, w * value, h, whitePaint); + canvas.restore(); + canvas.restore(); + + canvas.restore(); + } + + + private float lastTouchX; + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + if (w <= 0) { + return false; + } + + final float x = event.getX(); + if (event.getAction() == MotionEvent.ACTION_MOVE || event.getAction() == MotionEvent.ACTION_UP) { + float pastVolume = this.maxVolume - this.minVolume != 0 ? this.minVolume + this.value * (this.maxVolume - this.minVolume) : 0; + value = Utilities.clamp(value + (x - lastTouchX) / w, 1, 0); + final float volume = this.maxVolume - this.minVolume != 0 ? this.minVolume + this.value * (this.maxVolume - this.minVolume) : 0; + if (volume <= this.minVolume && pastVolume > volume || volume >= this.maxVolume && pastVolume < volume) { + try { + performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } catch (Exception ignore) {} + } else if (Math.floor(pastVolume * 5) != Math.floor(volume * 5)) { + try { + performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } catch (Exception ignore) {} + } + updateText(volume); + if (onValueChange != null) { + onValueChange.run(volume); + } + invalidate(); + } + lastTouchX = x; + return true; + } + + private void updateText(float volume) { + String string = Math.round(volume * 100) + "%"; + if (!TextUtils.equals(text.getText(), string)) { + text.cancelAnimation(); + text.setText(string); + } + } + + private float r; + private int w, h; + private final TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + r = dpf2(6.33f); + textPaint.setTextSize(dp(16)); + text.setTextSize(dp(15)); + // TODO: fix this nonsense + w = (int) Math.min(textPaint.measureText(LocaleController.getString(R.string.StoryAudioRemove)) + dp(88), MeasureSpec.getSize(widthMeasureSpec)); + h = dp(48); + setMeasuredDimension(w, h); + + final float cx = dp(25), cy = h / 2f; + + speaker1Paint.setPathEffect(new CornerPathEffect(dpf2(1.33f))); + speaker1Path.rewind(); + speaker1Path.moveTo(cx - dpf2(8.66f), cy - dpf2(2.9f)); + speaker1Path.lineTo(cx - dpf2(3f), cy - dpf2(2.9f)); + speaker1Path.lineTo(cx - dpf2(3f), cy + dpf2(2.9f)); + speaker1Path.lineTo(cx - dpf2(8.66f), cy + dpf2(2.9f)); + speaker1Path.close(); + + speaker2Paint.setPathEffect(new CornerPathEffect(dpf2(2.66f))); + speaker2Path.rewind(); + speaker2Path.moveTo(cx - dpf2(7.5f), cy); + speaker2Path.lineTo(cx, cy - dpf2(7.33f)); + speaker2Path.lineTo(cx, cy + dpf2(7.33f)); + speaker2Path.close(); + + speakerWave1Path.rewind(); + AndroidUtilities.rectTmp.set(cx - dpf2(0.33f) - dp(4.33f), cy - dp(4.33f), cx - dpf2(0.33f) + dp(4.33f), cy + dp(4.33f)); + speakerWave1Path.arcTo(AndroidUtilities.rectTmp, -60, 120); + speakerWave1Path.close(); + + speakerWave2Paint.setStyle(Paint.Style.STROKE); + speakerWave2Paint.setStrokeWidth(dp(2)); + speakerWave2Path.rewind(); + AndroidUtilities.rectTmp.set(cx - dpf2(0.33f) - dp(8), cy - dp(8), cx - dpf2(0.33f) + dp(8), cy + dp(8)); + speakerWave2Path.arcTo(AndroidUtilities.rectTmp, -70, 140); + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return who == text || super.verifyDrawable(who); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/TextureViewContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/TextureViewContainer.java new file mode 100644 index 0000000000..2981cb0065 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/TextureViewContainer.java @@ -0,0 +1,45 @@ +package org.telegram.ui; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.view.TextureView; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; + +import org.telegram.messenger.ImageReceiver; + +public class TextureViewContainer extends FrameLayout { + + ImageReceiver imageReceiver = new ImageReceiver(this); + boolean firstFrameRendered; + TextureView textureView; + + public TextureViewContainer(@NonNull Context context) { + super(context); + textureView = new TextureView(context); + addView(textureView); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + if (!firstFrameRendered) { + imageReceiver.setImageCoords(0, 0, getMeasuredWidth(), getMeasuredHeight()); + imageReceiver.draw(canvas); + } + super.dispatchDraw(canvas); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + imageReceiver.onAttachedToWindow(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + imageReceiver.onDetachedFromWindow(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/UsersSelectActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/UsersSelectActivity.java index b32c8b8d62..f43232302e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/UsersSelectActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/UsersSelectActivity.java @@ -734,7 +734,7 @@ public void afterTextChanged(Editable editable) { spansContainer.removeSpan(span); } else { if (!(object instanceof String) && (!getUserConfig().isPremium() && selectedCount >= MessagesController.getInstance(currentAccount).dialogFiltersChatsLimitDefault) || selectedCount >= MessagesController.getInstance(currentAccount).dialogFiltersChatsLimitPremium) { - LimitReachedBottomSheet limitReachedBottomSheet = new LimitReachedBottomSheet(this, context, LimitReachedBottomSheet.TYPE_CHATS_IN_FOLDER, currentAccount); + LimitReachedBottomSheet limitReachedBottomSheet = new LimitReachedBottomSheet(this, context, LimitReachedBottomSheet.TYPE_CHATS_IN_FOLDER, currentAccount, null); limitReachedBottomSheet.setCurrentValue(selectedCount); showDialog(limitReachedBottomSheet); return; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/WebAppDisclaimerAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/WebAppDisclaimerAlert.java new file mode 100644 index 0000000000..04ed92bc3b --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/WebAppDisclaimerAlert.java @@ -0,0 +1,88 @@ +package org.telegram.ui; + +import android.content.Context; +import android.content.DialogInterface; +import android.os.Build; +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 com.google.android.exoplayer2.util.Consumer; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.R; +import org.telegram.messenger.UserObject; +import org.telegram.messenger.browser.Browser; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.CheckBoxCell; +import org.telegram.ui.Components.LayoutHelper; + +public class WebAppDisclaimerAlert { + + + private CheckBoxCell cell; + private CheckBoxCell cell2; + private AlertDialog alert; + private TextView positiveButton; + + public static void show(Context context, Consumer consumer, TLRPC.User withSendMessage) { + WebAppDisclaimerAlert alert = new WebAppDisclaimerAlert(); + + AlertDialog.Builder alertDialog = new AlertDialog.Builder(context); + alertDialog.setTitle(LocaleController.getString("TermsOfUse", R.string.TermsOfUse)); + LinearLayout linearLayout = new LinearLayout(context); + linearLayout.setOrientation(LinearLayout.VERTICAL); + TextView textView = new TextView(context); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + textView.setLetterSpacing(0.025f); + } + textView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 24, 0, 24, 0)); + + alert.cell = new CheckBoxCell(context, 1, null); + alert.cell.getTextView().getLayoutParams().width = LayoutHelper.MATCH_PARENT; + alert.cell.getTextView().setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + linearLayout.addView(alert.cell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT, 8, 0, 8, 0)); + +// if (withSendMessage != null) { +// alert.cell2 = new CheckBoxCell(context, 1, null); +// alert.cell2.getTextView().setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); +// linearLayout.addView(alert.cell2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT, 8, -8, 8, 0)); +// alert.cell2.setText(AndroidUtilities.replaceTags(LocaleController.formatString("OpenUrlOption2", R.string.OpenUrlOption2, UserObject.getUserName(withSendMessage))), "", true, false); +// alert.cell2.setOnClickListener(v -> { +// alert.cell2.setChecked(!alert.cell2.isChecked(), true); +// }); +// } + + textView.setText(AndroidUtilities.replaceTags(LocaleController.getString("BotWebAppDisclaimerSubtitle", R.string.BotWebAppDisclaimerSubtitle))); + alert.cell.setText(AndroidUtilities.replaceSingleTag(LocaleController.getString("BotWebAppDisclaimerCheck", R.string.BotWebAppDisclaimerCheck), () -> { + Browser.openUrl(context, LocaleController.getString("WebAppDisclaimerUrl", R.string.WebAppDisclaimerUrl)); + }), "", false, false); + alertDialog.setView(linearLayout); + alertDialog.setPositiveButton(LocaleController.getString("Continue", R.string.Continue), (dialog, which) -> { + consumer.accept(true); + dialog.dismiss(); + }); + alertDialog.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), (dialog, which) -> { + dialog.dismiss(); + }); + alert.alert = alertDialog.create(); + alert.alert.show(); + alert.positiveButton = (TextView) alert.alert.getButton(DialogInterface.BUTTON_POSITIVE); + alert.positiveButton.setEnabled(false); + alert.positiveButton.setAlpha(0.5f); + alert.cell.setOnClickListener(v -> { + alert.cell.setChecked(!alert.cell.isChecked(), true); + alert.positiveButton.setEnabled(alert.cell.isChecked()); + alert.positiveButton.animate().alpha(alert.cell.isChecked() ? 1f : 0.5f).start(); + }); + alert.cell.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector), Theme.RIPPLE_MASK_ROUNDRECT_6DP)); + } +} diff --git a/TMessagesProj/src/main/res/drawable-hdpi/filled_add_photo.png b/TMessagesProj/src/main/res/drawable-hdpi/filled_add_photo.png new file mode 100644 index 0000000000..32cdf35f33 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/filled_add_photo.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/filled_fire.png b/TMessagesProj/src/main/res/drawable-hdpi/filled_fire.png new file mode 100644 index 0000000000..db7bb482d0 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/filled_fire.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/filled_limit_boost.png b/TMessagesProj/src/main/res/drawable-hdpi/filled_limit_boost.png new file mode 100644 index 0000000000..d1e6a8a77a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/filled_limit_boost.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/filled_views.png b/TMessagesProj/src/main/res/drawable-hdpi/filled_views.png new file mode 100644 index 0000000000..e005cbcfdd Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/filled_views.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/filled_widget_music.png b/TMessagesProj/src/main/res/drawable-hdpi/filled_widget_music.png new file mode 100644 index 0000000000..4e4c853849 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/filled_widget_music.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/media_crop.png b/TMessagesProj/src/main/res/drawable-hdpi/media_crop.png new file mode 100644 index 0000000000..8ed6d0c77c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/media_crop.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/media_draw.png b/TMessagesProj/src/main/res/drawable-hdpi/media_draw.png new file mode 100644 index 0000000000..40965a3945 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/media_draw.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/media_flip.png b/TMessagesProj/src/main/res/drawable-hdpi/media_flip.png new file mode 100644 index 0000000000..69b923bbe8 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/media_flip.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/media_settings.png b/TMessagesProj/src/main/res/drawable-hdpi/media_settings.png new file mode 100644 index 0000000000..24099817fa Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/media_settings.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/media_sticker.png b/TMessagesProj/src/main/res/drawable-hdpi/media_sticker.png new file mode 100644 index 0000000000..ee03ccda4d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/media_sticker.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/media_text.png b/TMessagesProj/src/main/res/drawable-hdpi/media_text.png new file mode 100644 index 0000000000..13a15cc993 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/media_text.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/menu_forward_check.png b/TMessagesProj/src/main/res/drawable-hdpi/menu_forward_check.png new file mode 100644 index 0000000000..6445f3ff13 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/menu_forward_check.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/mini_viewonce.png b/TMessagesProj/src/main/res/drawable-hdpi/mini_viewonce.png new file mode 100644 index 0000000000..ced9808677 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/mini_viewonce.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/msg_input_send_mini.png b/TMessagesProj/src/main/res/drawable-hdpi/msg_input_send_mini.png new file mode 100644 index 0000000000..252d0d4493 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/msg_input_send_mini.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/filled_add_photo.png b/TMessagesProj/src/main/res/drawable-mdpi/filled_add_photo.png new file mode 100644 index 0000000000..08ffd96568 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/filled_add_photo.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/filled_fire.png b/TMessagesProj/src/main/res/drawable-mdpi/filled_fire.png new file mode 100644 index 0000000000..d8e03f4fd1 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/filled_fire.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/filled_limit_boost.png b/TMessagesProj/src/main/res/drawable-mdpi/filled_limit_boost.png new file mode 100644 index 0000000000..f12300eda5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/filled_limit_boost.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/filled_views.png b/TMessagesProj/src/main/res/drawable-mdpi/filled_views.png new file mode 100644 index 0000000000..086ae690d2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/filled_views.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/filled_widget_music.png b/TMessagesProj/src/main/res/drawable-mdpi/filled_widget_music.png new file mode 100644 index 0000000000..12a01e491d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/filled_widget_music.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/media_crop.png b/TMessagesProj/src/main/res/drawable-mdpi/media_crop.png new file mode 100644 index 0000000000..f3fccccccf Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/media_crop.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/media_draw.png b/TMessagesProj/src/main/res/drawable-mdpi/media_draw.png new file mode 100644 index 0000000000..e673622295 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/media_draw.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/media_flip.png b/TMessagesProj/src/main/res/drawable-mdpi/media_flip.png new file mode 100644 index 0000000000..882a2f96de Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/media_flip.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/media_settings.png b/TMessagesProj/src/main/res/drawable-mdpi/media_settings.png new file mode 100644 index 0000000000..6f746da45a Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/media_settings.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/media_sticker.png b/TMessagesProj/src/main/res/drawable-mdpi/media_sticker.png new file mode 100644 index 0000000000..5e7bc56500 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/media_sticker.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/media_text.png b/TMessagesProj/src/main/res/drawable-mdpi/media_text.png new file mode 100644 index 0000000000..3cf213c735 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/media_text.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/menu_forward_check.png b/TMessagesProj/src/main/res/drawable-mdpi/menu_forward_check.png new file mode 100644 index 0000000000..7aafc1a487 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/menu_forward_check.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/mini_viewonce.png b/TMessagesProj/src/main/res/drawable-mdpi/mini_viewonce.png new file mode 100644 index 0000000000..02e6db33ac Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/mini_viewonce.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_input_send_mini.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_input_send_mini.png new file mode 100644 index 0000000000..32b576207f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/msg_input_send_mini.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/filled_add_photo.png b/TMessagesProj/src/main/res/drawable-xhdpi/filled_add_photo.png new file mode 100644 index 0000000000..da4c0838ad Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/filled_add_photo.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/filled_fire.png b/TMessagesProj/src/main/res/drawable-xhdpi/filled_fire.png new file mode 100644 index 0000000000..81b35e8893 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/filled_fire.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/filled_limit_boost.png b/TMessagesProj/src/main/res/drawable-xhdpi/filled_limit_boost.png new file mode 100644 index 0000000000..0694ba1a01 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/filled_limit_boost.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/filled_views.png b/TMessagesProj/src/main/res/drawable-xhdpi/filled_views.png new file mode 100644 index 0000000000..4e2f852bc4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/filled_views.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/filled_widget_music.png b/TMessagesProj/src/main/res/drawable-xhdpi/filled_widget_music.png new file mode 100644 index 0000000000..2049c4b15c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/filled_widget_music.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/media_crop.png b/TMessagesProj/src/main/res/drawable-xhdpi/media_crop.png new file mode 100644 index 0000000000..a303f38d58 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/media_crop.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/media_draw.png b/TMessagesProj/src/main/res/drawable-xhdpi/media_draw.png new file mode 100644 index 0000000000..f7567f2e74 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/media_draw.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/media_flip.png b/TMessagesProj/src/main/res/drawable-xhdpi/media_flip.png new file mode 100644 index 0000000000..5e4b9788a3 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/media_flip.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/media_settings.png b/TMessagesProj/src/main/res/drawable-xhdpi/media_settings.png new file mode 100644 index 0000000000..b358190fed Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/media_settings.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/media_sticker.png b/TMessagesProj/src/main/res/drawable-xhdpi/media_sticker.png new file mode 100644 index 0000000000..80bec68596 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/media_sticker.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/media_text.png b/TMessagesProj/src/main/res/drawable-xhdpi/media_text.png new file mode 100644 index 0000000000..3e7c91d966 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/media_text.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/menu_forward_check.png b/TMessagesProj/src/main/res/drawable-xhdpi/menu_forward_check.png new file mode 100644 index 0000000000..5535db908c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/menu_forward_check.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/mini_viewonce.png b/TMessagesProj/src/main/res/drawable-xhdpi/mini_viewonce.png new file mode 100644 index 0000000000..304fd80958 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/mini_viewonce.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_input_send_mini.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_input_send_mini.png new file mode 100644 index 0000000000..0279dde617 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/msg_input_send_mini.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/filled_add_photo.png b/TMessagesProj/src/main/res/drawable-xxhdpi/filled_add_photo.png new file mode 100644 index 0000000000..460b7929fa Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/filled_add_photo.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/filled_fire.png b/TMessagesProj/src/main/res/drawable-xxhdpi/filled_fire.png new file mode 100644 index 0000000000..f19456be55 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/filled_fire.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/filled_limit_boost.png b/TMessagesProj/src/main/res/drawable-xxhdpi/filled_limit_boost.png new file mode 100644 index 0000000000..29f506bc5b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/filled_limit_boost.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/filled_views.png b/TMessagesProj/src/main/res/drawable-xxhdpi/filled_views.png new file mode 100644 index 0000000000..391295db6e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/filled_views.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/filled_widget_music.png b/TMessagesProj/src/main/res/drawable-xxhdpi/filled_widget_music.png new file mode 100644 index 0000000000..0811211ce9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/filled_widget_music.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/media_crop.png b/TMessagesProj/src/main/res/drawable-xxhdpi/media_crop.png new file mode 100644 index 0000000000..1291a11302 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/media_crop.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/media_draw.png b/TMessagesProj/src/main/res/drawable-xxhdpi/media_draw.png new file mode 100644 index 0000000000..2b3aa9ddf6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/media_draw.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/media_flip.png b/TMessagesProj/src/main/res/drawable-xxhdpi/media_flip.png new file mode 100644 index 0000000000..2468172bef Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/media_flip.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/media_settings.png b/TMessagesProj/src/main/res/drawable-xxhdpi/media_settings.png new file mode 100644 index 0000000000..db77cff2d8 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/media_settings.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/media_sticker.png b/TMessagesProj/src/main/res/drawable-xxhdpi/media_sticker.png new file mode 100644 index 0000000000..fdd33532f5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/media_sticker.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/media_text.png b/TMessagesProj/src/main/res/drawable-xxhdpi/media_text.png new file mode 100644 index 0000000000..ae16a8fb55 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/media_text.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/menu_forward_check.png b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_forward_check.png new file mode 100644 index 0000000000..46fcbfc5c8 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_forward_check.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/mini_viewonce.png b/TMessagesProj/src/main/res/drawable-xxhdpi/mini_viewonce.png new file mode 100644 index 0000000000..f962825e93 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/mini_viewonce.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_input_send_mini.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_input_send_mini.png new file mode 100644 index 0000000000..937d5ecea7 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_input_send_mini.png differ diff --git a/TMessagesProj/src/main/res/raw/blur_frg.glsl b/TMessagesProj/src/main/res/raw/blur_frg.glsl new file mode 100644 index 0000000000..3a4711b37f --- /dev/null +++ b/TMessagesProj/src/main/res/raw/blur_frg.glsl @@ -0,0 +1,51 @@ +precision mediump float; + +varying highp vec2 uv; +uniform sampler2D tex; +uniform mat4 matrix; +uniform vec2 texSz; +uniform vec2 sz; +uniform int step; +uniform float flipy; + +uniform float hasVideoMatrix; +uniform mat4 videoMatrix; + +uniform vec4 gtop; +uniform vec4 gbottom; + +vec4 at(vec2 p) { + if (step != 0) { + return texture2D(tex, p); + } + vec2 uv = (matrix * vec4(clamp(p, 0., 1.), 0., 1.)).xy; + if (uv.x < 0. || uv.y < 0. || uv.x > 1. || uv.y > 1.) { + return mix(gtop, gbottom, p.y); + } + if (hasVideoMatrix > 0.5) { + if (flipy > .5) { + uv.y = 1. - uv.y; + } + return texture2D(tex, (videoMatrix * vec4(uv, 0., 1.)).xy); + } else { + return texture2D(tex, uv); + } +} + +#define pow2(x) (x * x) +const float pi = 3.14; + +void main() { + float r = 2.; + vec2 d = step == 0 ? 1. / (texSz / sz) : 1. / sz * r; + vec2 st = d / r; + vec4 col = vec4(0.); + float count = 0.; + for (float x = -d.x; x < d.x; x += st.x) + for (float y = -d.y; y < d.y; y += st.y) + { + col += at(uv + vec2(x, y)); + count++; + } + gl_FragColor = vec4((col / count).rgb, 1.); +} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/blur_vrt.glsl b/TMessagesProj/src/main/res/raw/blur_vrt.glsl new file mode 100644 index 0000000000..c81559c9c7 --- /dev/null +++ b/TMessagesProj/src/main/res/raw/blur_vrt.glsl @@ -0,0 +1,8 @@ +attribute vec4 p; +attribute vec2 inputuv; +varying vec2 uv; + +void main() { + gl_Position = p; + uv = inputuv; +} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/spoiler_fragment.glsl b/TMessagesProj/src/main/res/raw/spoiler_fragment.glsl new file mode 100644 index 0000000000..832e9f964d --- /dev/null +++ b/TMessagesProj/src/main/res/raw/spoiler_fragment.glsl @@ -0,0 +1,15 @@ +#version 300 es + +precision highp float; + +in float alpha; +out vec4 fragColor; + +void main() { + vec2 circCoord = 2.0 * gl_PointCoord - 1.0; + if (dot(circCoord, circCoord) > 1.0) { + discard; + } + + fragColor = vec4(1., 1., 1., alpha); +} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/spoiler_vertex.glsl b/TMessagesProj/src/main/res/raw/spoiler_vertex.glsl new file mode 100644 index 0000000000..3c87b82fef --- /dev/null +++ b/TMessagesProj/src/main/res/raw/spoiler_vertex.glsl @@ -0,0 +1,135 @@ +#version 300 es + +precision highp float; + +layout(location = 0) in vec2 inPosition; +layout(location = 1) in vec2 inVelocity; +layout(location = 2) in float inTime; +layout(location = 3) in float inDuration; + +out vec2 outPosition; +out vec2 outVelocity; +out float outTime; +out float outDuration; + +out float alpha; + +uniform float reset; +uniform float time; +uniform float deltaTime; +uniform vec2 size; +uniform float r; +uniform float seed; +uniform float noiseScale; +uniform float noiseSpeed; +uniform float noiseMovement; +uniform float dampingMult; +uniform float forceMult; +uniform float velocityMult; +uniform float longevity; +uniform float maxVelocity; + +float rand(vec2 n) { + return fract(sin(dot(n,vec2(12.9898,4.1414-seed*.42)))*4375.5453); +} +vec4 loop(vec4 p) { + p.xy = fract(p.xy / noiseScale) * noiseScale; + p.zw = fract(p.zw / noiseScale) * noiseScale; + return p; +} +vec3 loop(vec3 p) { + p.xy = fract(p.xy / noiseScale) * noiseScale; + return p; +} +float mod289(float x){return x-floor(x*(1./(289.+seed)))*(289.+seed);} +vec4 mod289(vec4 x){return x-floor(x*(1./(289.+seed)))*(289.0+seed);} +vec4 perm(vec4 x){return mod289(((x*34.)+1.)*x);} +float noise(vec3 p){ + + vec3 a = floor(p); + vec3 d = p - a; + d = d * d * (3. - 2. * d); + + vec4 b = a.xxyy + vec4(0., 1., 0., 1.); + vec4 k1 = perm(loop(b.xyxy)); + vec4 k2 = perm(loop(k1.xyxy + b.zzww)); + + vec4 c = k2 + a.zzzz; + vec4 k3 = perm(c); + vec4 k4 = perm(c + 1.0); + + vec4 o3 = fract(k4 / 41.0) * d.z + fract(k3 / 41.0) * (1.0 - d.z); + vec2 o4 = o3.yw * d.x + o3.xz * (1.0 - d.x); + + return o4.y * d.y + o4.x * (1.0 - d.y); +} +vec3 grad(vec3 p) { + const vec2 e = vec2(.1, .0); + return vec3( + noise(loop(p + e.xyy)) - noise(loop(p - e.xyy)), + noise(loop(p + e.yxy)) - noise(loop(p - e.yxy)), + noise(loop(p + e.yyx)) - noise(loop(p - e.yyx)) + ) / (2.0 * e.x); +} +vec3 curlNoise(vec3 p) { + p.xy /= size; + p.x *= (size.x / size.y); + p.xy = fract(p.xy); + p.xy *= noiseScale; + + const vec2 e = vec2(.01, .0); + return grad(loop(p)).yzx - vec3( + grad(loop(p + e.yxy)).z, + grad(loop(p + e.yyx)).x, + grad(loop(p + e.xyy)).y + ); +} + +void main() { + vec2 position = inPosition; + vec2 velocity = inVelocity; + float particleDuration = inDuration; + float particleTime = inTime + deltaTime * particleDuration / longevity; + + if (reset > 0.) { + particleTime = rand(vec2(-94.3, 83.9) * vec2(gl_VertexID, gl_VertexID)); + particleDuration = .5 + 2. * rand(vec2(gl_VertexID) + seed * 32.4); + position = size * vec2( + rand(vec2(42., -3.) * vec2(cos(float(gl_VertexID) - seed), gl_VertexID)), + rand(vec2(-3., 42.) * vec2(time * time, sin(float(gl_VertexID) + seed))) + ); + velocity = vec2(0.); + } else if (particleTime >= 1.) { + particleTime = 0.0; + particleDuration = .5 + 2. * rand(vec2(gl_VertexID) + position); + velocity = vec2(0.); + } + + float msz = min(size.x, size.y); + vec2 force = normalize(curlNoise( + vec3( + position + time * (noiseMovement / 100. * msz), + time * noiseSpeed + rand(position) * 2.5 + ) + ).xy); + + velocity += force * forceMult * deltaTime * msz * .1; + velocity *= dampingMult; + float vlen = length(velocity); + float maxVelocityPx = maxVelocity / 100. * msz; + if (vlen > maxVelocityPx) { + velocity = velocity / vlen * maxVelocityPx; + } + + position += velocity * velocityMult * deltaTime; + position = fract(position / size) * size; + + outPosition = position; + outVelocity = velocity; + outTime = particleTime; + outDuration = particleDuration; + + gl_PointSize = r; + gl_Position = vec4((position / size * 2.0 - vec2(1.0)), 0.0, 1.0); + alpha = sin(particleTime * 3.14) * (.6 + .4 * rand(vec2(gl_VertexID))); +} diff --git a/TMessagesProj/src/main/res/values/strings.xml b/TMessagesProj/src/main/res/values/strings.xml index 692f0601f7..717c1a8f3a 100644 --- a/TMessagesProj/src/main/res/values/strings.xml +++ b/TMessagesProj/src/main/res/values/strings.xml @@ -620,6 +620,9 @@ Post Messages Edit Messages of Others Delete Messages of Others + Post Stories + Edit Stories of Others + Delete Stories of Others Delete Messages Add New Admins Send Anonymously @@ -2238,6 +2241,7 @@ Terms of Service By signing up,\nyou agree to the *Terms of Service*. https://telegram.org/privacy + https://telegram.org/tos/mini-apps Delete language pack Are you sure you want to delete **%1$s** language pack? Incorrect localization file @@ -3666,7 +3670,7 @@ 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. - You allowed this bot to message you in its web-app + You allowed this bot to message you in web-app %1$s received the following documents: %2$s Personal details Address @@ -5748,7 +5752,7 @@ Do you want to start app **%1$s** from bot **%2$s**?\n\nIt will be able to access your **IP address** and basic device info. This bot can\'t be added to the attachment menu. This bot is already in your attachment menu. - Remove **%1$s** from the attachment menu? + This will remove **%1$s** shortcuts from all menus. Remove **%1$s** from suggestions? Allow **%1$s** to access to your location?\n\nThe developer of **%1$s** will be able to access your location when this web app is open. Allow **%1$s** to access to your location?\n\nThe developer of **%1$s** will be able to access your location when this web app is open.\n\nGo to Settings > Permissions and turn **Location** on to share location data. @@ -6153,6 +6157,10 @@ Self-destruct timer has been disabled in %s. You can also set your default **self-destruct timer** for all chats in Settings. Automatically delete messages in this group for everyone after a period of time. + %1$s and %2$s + %1$s, %2$s and %3$s + %1$s, %2$s, %3$s and %4$s + %1$s, %2$s, %3$s, %4$s and %5$s **un1** uses a self-destruct timer for all chats.\nAll new messages in this chat will be automatically deleted after **%1$s** they’ve been sent. **You** set a self-destruct timer for all chats.\nAll new messages in this chat will be automatically deleted after **%s** they’ve been sent. You need admin rights in this group to enable auto-delete. @@ -6715,6 +6723,7 @@ Stories Archive Archive Only you can see archived stories unless you choose to save them to your profile. + Only admins can see archived stories unless you choose to post them to channel profile. %d archived stories %d archived story %d archived stories @@ -6865,7 +6874,9 @@ This story is shown to %d contacts. This story is shown to %d contacts. This story is pinned to your profile. + This story is pinned to channel posts. This story is hidden from your profile. + This story is hidden from channel posts. Draft Drafts Keep Draft @@ -6876,6 +6887,9 @@ From **%s** Choose how long the story will be visible. Keep Always + Choose how long the media will be kept after opening. + Do Not Delete + View Once Stories of **%s** were moved to **Archive**. Stories of **%s** were moved to **Chats**. Story shared @@ -6888,6 +6902,8 @@ %d Drafts Sound is now muted. Sound is now not muted. + Original audio will be removed. + Original audio will be preserved. Draft saved Uploading story… %s shared a story with you @@ -6966,12 +6982,21 @@ 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. + Post to Channel Page + Keep this story on the channel page even after it expires in %d hours. + Keep this story on the channel page even after it expires in %d hour. + Keep this story on the channel page even after it expires in %d hours. + Keep this story on the channel page even after it expires in %d hours. + Keep this story on the channel page even after it expires in %d hours. + Keep this story on the channel page even after it expires in %d hours. 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. + Users allowed to view your story will see it on the channel after it expires. The story will disappear 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. @@ -7048,6 +7073,7 @@ Hide Stories Unhide Stories Subscribe to **Telegram Premium** to add links and text formatting to your stories. + Subscribe to **Telegram Premium** to add up to 5 reactions and tags to a story. Maximum Length Reach Increase this limit **%1$d** times to **%2$s** symbols by subscribing to __Telegram Premium.__ To unlock viewers’ lists for expired and saved stories, subscribe to **Telegram Premium.** @@ -7119,5 +7145,50 @@ You can post **%1$d** stories in a month.\nSubscribe to **Telegram Premium** to increase this limit to **%2$d**. Sorry, you can’t post more than **%1$d** stories in a month. Deselect All - ADD LOCATION + Location + Audio + Photo + Send reaction as a private message + Remove Audio + Style + Archived Stories + Save to Posts + Remove from Posts + Photo will be kept. + Video will be kept. + Photo set to view once + Video set to view once + This video can only be viewed once. + This photo can only be viewed once. + Photo will be deleted in **%d** second after opening. + Photo will be deleted in **%d** seconds after opening. + Video will be deleted in **%d** second after opening. + Video will be deleted in **%d** seconds after opening. + Someone just got access to your messages! + Yes, it’s me + No, it’s not me! + We detected a new login to your account from %s. Is it you? + We detected new %d login to your account. Is it you? + We detected new %d logins to your account. Is it you? + We detected new %d login to your account from %s. Is it you? + We detected new %d logins to your account from %s. Is it you? + New Login Prevented + New Logins Prevented + We have terminated the login attempt from %s. + We have terminated the login attempts from: %s + Never send your login code to anyone or you can lose your Telegram account! + New Login Allowed + You can check the list of your active logins in **Settings > Devices**. + Manage Messages + Manage Stories + Premium subscribers + You are about to use a mini app operated by an independent party **not affiliated with Telegram**. You must agree to the Terms of Use of mini apps to continue. + I agree to the **Terms of Use** + Saved stories can be viewed by others on the channle’s profile until an admin removes them. + Premuim needed! + Replace + **%s** shortcut added in attachment menu and side menu. + **%s** shortcut added in side menu. + **%s** shortcut added in attachment menu. + Terms of Use