diff --git a/TMessagesProj/jni/CMakeLists.txt b/TMessagesProj/jni/CMakeLists.txt index 2e5963d9d..137d17f21 100644 --- a/TMessagesProj/jni/CMakeLists.txt +++ b/TMessagesProj/jni/CMakeLists.txt @@ -435,7 +435,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 0a9477813..3d108e2e9 100644 --- a/TMessagesProj/jni/TgNetWrapper.cpp +++ b/TMessagesProj/jni/TgNetWrapper.cpp @@ -126,7 +126,7 @@ void sendRequest(JNIEnv *env, jclass c, jint instanceNum, jlong object, jobject } } if (onComplete != nullptr) { - jniEnv[instanceNum]->CallVoidMethod(onComplete, jclass_RequestDelegateInternal_run, ptr, errorCode, errorText, networkType, responseTime); + jniEnv[instanceNum]->CallVoidMethod(onComplete, jclass_RequestDelegateInternal_run, ptr, errorCode, errorText, networkType, responseTime, msgId); } if (errorText != nullptr) { jniEnv[instanceNum]->DeleteLocalRef(errorText); diff --git a/TMessagesProj/jni/audio.c b/TMessagesProj/jni/audio.c index 55f1ec5a3..dcceaa107 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/ffmpeg/arm64-v8a/libavcodec.a b/TMessagesProj/jni/ffmpeg/arm64-v8a/libavcodec.a index 2154dd110..24e3dbf71 100644 Binary files a/TMessagesProj/jni/ffmpeg/arm64-v8a/libavcodec.a and b/TMessagesProj/jni/ffmpeg/arm64-v8a/libavcodec.a differ diff --git a/TMessagesProj/jni/ffmpeg/arm64-v8a/libavformat.a b/TMessagesProj/jni/ffmpeg/arm64-v8a/libavformat.a index edc96ecb5..8d9cfe961 100644 Binary files a/TMessagesProj/jni/ffmpeg/arm64-v8a/libavformat.a and b/TMessagesProj/jni/ffmpeg/arm64-v8a/libavformat.a differ diff --git a/TMessagesProj/jni/ffmpeg/arm64-v8a/libavresample.a b/TMessagesProj/jni/ffmpeg/arm64-v8a/libavresample.a index e82f0ca67..ab291dfff 100644 Binary files a/TMessagesProj/jni/ffmpeg/arm64-v8a/libavresample.a and b/TMessagesProj/jni/ffmpeg/arm64-v8a/libavresample.a differ diff --git a/TMessagesProj/jni/ffmpeg/arm64-v8a/libavutil.a b/TMessagesProj/jni/ffmpeg/arm64-v8a/libavutil.a index 28d13b604..ed769bf93 100644 Binary files a/TMessagesProj/jni/ffmpeg/arm64-v8a/libavutil.a and b/TMessagesProj/jni/ffmpeg/arm64-v8a/libavutil.a differ diff --git a/TMessagesProj/jni/ffmpeg/arm64-v8a/libswresample.a b/TMessagesProj/jni/ffmpeg/arm64-v8a/libswresample.a index f49883963..f1961df86 100644 Binary files a/TMessagesProj/jni/ffmpeg/arm64-v8a/libswresample.a and b/TMessagesProj/jni/ffmpeg/arm64-v8a/libswresample.a differ diff --git a/TMessagesProj/jni/ffmpeg/arm64-v8a/libswscale.a b/TMessagesProj/jni/ffmpeg/arm64-v8a/libswscale.a index af6db735b..8978d03c1 100644 Binary files a/TMessagesProj/jni/ffmpeg/arm64-v8a/libswscale.a and b/TMessagesProj/jni/ffmpeg/arm64-v8a/libswscale.a differ diff --git a/TMessagesProj/jni/ffmpeg/arm64-v8a/libvpx.a b/TMessagesProj/jni/ffmpeg/arm64-v8a/libvpx.a index a03f8d037..dece77606 100644 Binary files a/TMessagesProj/jni/ffmpeg/arm64-v8a/libvpx.a and b/TMessagesProj/jni/ffmpeg/arm64-v8a/libvpx.a differ diff --git a/TMessagesProj/jni/ffmpeg/armeabi-v7a/libavcodec.a b/TMessagesProj/jni/ffmpeg/armeabi-v7a/libavcodec.a index 1e84b9a9a..5c718f80b 100644 Binary files a/TMessagesProj/jni/ffmpeg/armeabi-v7a/libavcodec.a and b/TMessagesProj/jni/ffmpeg/armeabi-v7a/libavcodec.a differ diff --git a/TMessagesProj/jni/ffmpeg/armeabi-v7a/libavformat.a b/TMessagesProj/jni/ffmpeg/armeabi-v7a/libavformat.a index 474ed236f..70b17572b 100644 Binary files a/TMessagesProj/jni/ffmpeg/armeabi-v7a/libavformat.a and b/TMessagesProj/jni/ffmpeg/armeabi-v7a/libavformat.a differ diff --git a/TMessagesProj/jni/ffmpeg/armeabi-v7a/libavresample.a b/TMessagesProj/jni/ffmpeg/armeabi-v7a/libavresample.a index 043d80921..5a5f98f13 100644 Binary files a/TMessagesProj/jni/ffmpeg/armeabi-v7a/libavresample.a and b/TMessagesProj/jni/ffmpeg/armeabi-v7a/libavresample.a differ diff --git a/TMessagesProj/jni/ffmpeg/armeabi-v7a/libavutil.a b/TMessagesProj/jni/ffmpeg/armeabi-v7a/libavutil.a index de439a1c9..7e2cb772e 100644 Binary files a/TMessagesProj/jni/ffmpeg/armeabi-v7a/libavutil.a and b/TMessagesProj/jni/ffmpeg/armeabi-v7a/libavutil.a differ diff --git a/TMessagesProj/jni/ffmpeg/armeabi-v7a/libswresample.a b/TMessagesProj/jni/ffmpeg/armeabi-v7a/libswresample.a index 7a674f20c..f6c9ad6ce 100644 Binary files a/TMessagesProj/jni/ffmpeg/armeabi-v7a/libswresample.a and b/TMessagesProj/jni/ffmpeg/armeabi-v7a/libswresample.a differ diff --git a/TMessagesProj/jni/ffmpeg/armeabi-v7a/libswscale.a b/TMessagesProj/jni/ffmpeg/armeabi-v7a/libswscale.a index f016a21b7..841227ea5 100644 Binary files a/TMessagesProj/jni/ffmpeg/armeabi-v7a/libswscale.a and b/TMessagesProj/jni/ffmpeg/armeabi-v7a/libswscale.a differ diff --git a/TMessagesProj/jni/ffmpeg/armeabi-v7a/libvpx.a b/TMessagesProj/jni/ffmpeg/armeabi-v7a/libvpx.a index 59b8bea30..dfbdd7c27 100644 Binary files a/TMessagesProj/jni/ffmpeg/armeabi-v7a/libvpx.a and b/TMessagesProj/jni/ffmpeg/armeabi-v7a/libvpx.a differ diff --git a/TMessagesProj/jni/ffmpeg/build_ffmpeg/build_ffmpeg.sh b/TMessagesProj/jni/ffmpeg/build_ffmpeg/build_ffmpeg.sh old mode 100644 new mode 100755 index baee1efde..250ef0d5d --- a/TMessagesProj/jni/ffmpeg/build_ffmpeg/build_ffmpeg.sh +++ b/TMessagesProj/jni/ffmpeg/build_ffmpeg/build_ffmpeg.sh @@ -87,10 +87,12 @@ LIBS=" -L${PREFIX}/lib" --enable-decoder=gif \ --enable-decoder=alac \ --enable-decoder=opus \ +--enable-decoder=mp3 \ --enable-demuxer=mov \ --enable-demuxer=gif \ --enable-demuxer=ogg \ --enable-demuxer=matroska \ +--enable-demuxer=mp3 \ --enable-hwaccels \ --enable-runtime-cpudetect \ $ADDITIONAL_CONFIGURE_FLAG diff --git a/TMessagesProj/jni/ffmpeg/x86/libavcodec.a b/TMessagesProj/jni/ffmpeg/x86/libavcodec.a index 1dc7123da..308f62fea 100644 Binary files a/TMessagesProj/jni/ffmpeg/x86/libavcodec.a and b/TMessagesProj/jni/ffmpeg/x86/libavcodec.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86/libavformat.a b/TMessagesProj/jni/ffmpeg/x86/libavformat.a index 6adef60b5..472c2f6e6 100644 Binary files a/TMessagesProj/jni/ffmpeg/x86/libavformat.a and b/TMessagesProj/jni/ffmpeg/x86/libavformat.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86/libavresample.a b/TMessagesProj/jni/ffmpeg/x86/libavresample.a index 3f5ec1c48..a7b9db453 100644 Binary files a/TMessagesProj/jni/ffmpeg/x86/libavresample.a and b/TMessagesProj/jni/ffmpeg/x86/libavresample.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86/libavutil.a b/TMessagesProj/jni/ffmpeg/x86/libavutil.a index 568f42a52..e718998b0 100644 Binary files a/TMessagesProj/jni/ffmpeg/x86/libavutil.a and b/TMessagesProj/jni/ffmpeg/x86/libavutil.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86/libswresample.a b/TMessagesProj/jni/ffmpeg/x86/libswresample.a index 7b2f6f6ca..07ca70603 100644 Binary files a/TMessagesProj/jni/ffmpeg/x86/libswresample.a and b/TMessagesProj/jni/ffmpeg/x86/libswresample.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86/libswscale.a b/TMessagesProj/jni/ffmpeg/x86/libswscale.a index ec4e3c0fb..08e4d1d00 100644 Binary files a/TMessagesProj/jni/ffmpeg/x86/libswscale.a and b/TMessagesProj/jni/ffmpeg/x86/libswscale.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86/libvpx.a b/TMessagesProj/jni/ffmpeg/x86/libvpx.a index 7ba38e4c3..73f57d4fd 100644 Binary files a/TMessagesProj/jni/ffmpeg/x86/libvpx.a and b/TMessagesProj/jni/ffmpeg/x86/libvpx.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86_64/libavcodec.a b/TMessagesProj/jni/ffmpeg/x86_64/libavcodec.a index 2794244f9..dd7faf7d9 100644 Binary files a/TMessagesProj/jni/ffmpeg/x86_64/libavcodec.a and b/TMessagesProj/jni/ffmpeg/x86_64/libavcodec.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86_64/libavformat.a b/TMessagesProj/jni/ffmpeg/x86_64/libavformat.a index 3b6660f0c..eaaa6503a 100644 Binary files a/TMessagesProj/jni/ffmpeg/x86_64/libavformat.a and b/TMessagesProj/jni/ffmpeg/x86_64/libavformat.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86_64/libavresample.a b/TMessagesProj/jni/ffmpeg/x86_64/libavresample.a index 9e11bb46c..d3d2464e0 100644 Binary files a/TMessagesProj/jni/ffmpeg/x86_64/libavresample.a and b/TMessagesProj/jni/ffmpeg/x86_64/libavresample.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86_64/libavutil.a b/TMessagesProj/jni/ffmpeg/x86_64/libavutil.a index 36edeed7c..e75b2dc02 100644 Binary files a/TMessagesProj/jni/ffmpeg/x86_64/libavutil.a and b/TMessagesProj/jni/ffmpeg/x86_64/libavutil.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86_64/libswresample.a b/TMessagesProj/jni/ffmpeg/x86_64/libswresample.a index 57126db54..494b46ef7 100644 Binary files a/TMessagesProj/jni/ffmpeg/x86_64/libswresample.a and b/TMessagesProj/jni/ffmpeg/x86_64/libswresample.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86_64/libswscale.a b/TMessagesProj/jni/ffmpeg/x86_64/libswscale.a index ba187bf91..019c08c13 100644 Binary files a/TMessagesProj/jni/ffmpeg/x86_64/libswscale.a and b/TMessagesProj/jni/ffmpeg/x86_64/libswscale.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86_64/libvpx.a b/TMessagesProj/jni/ffmpeg/x86_64/libvpx.a index 9e9b5a2f4..eb7754ed4 100644 Binary files a/TMessagesProj/jni/ffmpeg/x86_64/libvpx.a and b/TMessagesProj/jni/ffmpeg/x86_64/libvpx.a differ diff --git a/TMessagesProj/jni/tgnet/ConnectionSocket.cpp b/TMessagesProj/jni/tgnet/ConnectionSocket.cpp index 8d246ba7b..0315aad5c 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ private: 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 14f90e5f6..4227d64e0 100644 --- a/TMessagesProj/jni/tgnet/ConnectionsManager.cpp +++ b/TMessagesProj/jni/tgnet/ConnectionsManager.cpp @@ -970,7 +970,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 { @@ -1128,7 +1128,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); @@ -1643,7 +1643,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 40bb5d17c..4f37671f2 100644 --- a/TMessagesProj/proguard-rules.pro +++ b/TMessagesProj/proguard-rules.pro @@ -26,6 +26,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 { *; } # https://developers.google.com/ml-kit/known-issues#android_issues -keep class com.google.mlkit.nl.languageid.internal.LanguageIdentificationJni { *; } diff --git a/TMessagesProj/src/main/assets/arctic.attheme b/TMessagesProj/src/main/assets/arctic.attheme index b49ae5827..955d1070c 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 7f42dfe75..e1b88a0e8 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 a5aab9930..6944f3d31 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 000000000..320ce49b2 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 2d57cde4d..28941bb69 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 79b3ff8b6..c470a1792 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java @@ -945,8 +945,8 @@ public class AndroidUtilities { } } - 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); @@ -2291,10 +2291,8 @@ public class AndroidUtilities { } 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); @@ -5379,6 +5377,18 @@ public class AndroidUtilities { 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 5880c7bb9..a5297e449 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java @@ -150,6 +150,7 @@ public class ApplicationLoader extends Application { return; } applicationInited = true; + NativeLoader.initNativeLibs(ApplicationLoader.applicationContext); try { LocaleController.getInstance(); //TODO improve @@ -228,7 +229,6 @@ public class ApplicationLoader extends Application { ContactsController.getInstance(a).checkAppAccount(); DownloadController.getInstance(a); } - ChatThemeController.init(); BillingController.getInstance().startConnection(); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java b/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java index 8edbc1cb9..918141694 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java @@ -24,8 +24,8 @@ public class BuildVars { public static boolean USE_CLOUD_STRINGS = true; public static boolean CHECK_UPDATES = true; public static boolean NO_SCOPED_STORAGE = Build.VERSION.SDK_INT <= 29; - public static int BUILD_VERSION = 3804; - public static String BUILD_VERSION_STRING = "10.0.5"; + public static int BUILD_VERSION = 3867; + public static String BUILD_VERSION_STRING = "10.0.8"; public static int APP_ID = 4; public static String APP_HASH = "014b35b6184100b085b0d0572f9b5103"; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ChatObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/ChatObject.java index dbb810169..a6d1c9613 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 class ChatObject { 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 191ef974e..1726ba893 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ChatThemeController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ChatThemeController.java @@ -24,15 +24,20 @@ import java.util.Locale; 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; @@ -53,20 +58,20 @@ public class ChatThemeController extends BaseController { } } - 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(() -> { @@ -89,7 +94,7 @@ public class ChatThemeController extends BaseController { 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); } @@ -107,7 +112,7 @@ public class ChatThemeController extends BaseController { } 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(); @@ -122,7 +127,7 @@ public class ChatThemeController extends BaseController { 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(); @@ -131,15 +136,15 @@ public class ChatThemeController extends BaseController { } } - 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); @@ -149,7 +154,7 @@ public class ChatThemeController extends BaseController { 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); @@ -158,7 +163,7 @@ public class ChatThemeController extends BaseController { 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; @@ -201,10 +206,6 @@ public class ChatThemeController extends BaseController { 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; @@ -311,7 +312,7 @@ public class ChatThemeController extends BaseController { 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) { @@ -325,7 +326,7 @@ public class ChatThemeController extends BaseController { } } - 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) { @@ -343,15 +344,15 @@ public class ChatThemeController extends BaseController { } } - 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; @@ -375,11 +376,11 @@ public class ChatThemeController extends BaseController { }); } - 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 { @@ -392,7 +393,7 @@ public class ChatThemeController extends BaseController { }); } - 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 9011aff2f..b63f4a9bd 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 class DatabaseMigrationHelper { 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 1fb787d2d..7f14790c7 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java @@ -269,6 +269,7 @@ public class FileLoadOperation { 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() { @@ -948,7 +949,7 @@ public class FileLoadOperation { } } 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 af76fdd3e..2c1855d0b 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 @@ package org.telegram.messenger; 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; @@ -123,6 +121,41 @@ public class FileLoader extends BaseController { 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); @@ -924,13 +957,18 @@ public class FileLoader extends BaseController { @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 e5b7f4b11..fcae4b5df 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 @@ import java.util.concurrent.CountDownLatch; 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 class FilePathDatabase { 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 @@ public class FilePathDatabase { 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 class FilePathDatabase { } } - 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 class FilePathDatabase { 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 @@ public class FilePathDatabase { } } + 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 698fc0e8f..c4c8a7607 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java @@ -911,7 +911,35 @@ public class FileRefController extends BaseController { } } 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 b99a1a45a..dbe256405 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 class FileStreamLoadOperation extends BaseDataSource implements FileLoadO try { file = new RandomAccessFile(currentFile, "r"); file.seek(currentOffset); + if (loadOperation.isFinished()) { + bytesRemaining = currentFile.length() - currentOffset; + } } catch (Throwable e) { } } @@ -163,6 +166,9 @@ public class FileStreamLoadOperation extends BaseDataSource implements FileLoadO 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 2a2c73c54..ab82c6238 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ImageReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ImageReceiver.java @@ -127,6 +127,10 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg recycleOnRelease = true; } + public String getKey() { + return key; + } + public int getWidth() { return bitmap != null ? bitmap.getWidth() : 0; } @@ -3090,7 +3094,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg 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 3cd122e16..7063e2e12 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java @@ -120,6 +120,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(); @@ -293,8 +295,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, 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 ); } } @@ -5278,7 +5279,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, } 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); @@ -5315,7 +5316,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, 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, @@ -5324,6 +5325,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, needCompress, duration, info.filterState, info.paintPath, + info.blurPath, info.mediaEntities, info.isPhoto, info.cropState, @@ -5334,8 +5336,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, 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 c916729ae..22d980f26 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.TextUtils; 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; @@ -99,7 +98,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 class MediaDataController extends BaseController { 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 class MediaDataController extends BaseController { 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 class MediaDataController extends BaseController { } } - 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); } } @@ -460,6 +463,9 @@ public class MediaDataController extends BaseController { } 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(() -> { @@ -481,6 +487,13 @@ public class MediaDataController extends BaseController { 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 { @@ -501,6 +514,9 @@ public class MediaDataController extends BaseController { TLRPC.TL_attachMenuBots r = (TLRPC.TL_attachMenuBots) response; processLoadedMenuBots(r, r.hash, date, false); } + if (onDone != null) { + AndroidUtilities.runOnUIThread(onDone); + } }); } } @@ -510,19 +526,35 @@ public class MediaDataController extends BaseController { 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 { @@ -731,18 +763,18 @@ public class MediaDataController extends BaseController { 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) { @@ -1283,14 +1315,21 @@ public class MediaDataController extends BaseController { }); } + 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 @@ -1509,6 +1548,16 @@ public class MediaDataController extends BaseController { 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) { @@ -2360,7 +2409,7 @@ public class MediaDataController extends BaseController { 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); } } } @@ -7143,7 +7192,7 @@ public class MediaDataController extends BaseController { })); } - public void chekAllMedia(boolean force) { + public void checkAllMedia(boolean force) { if (force) { reactionsUpdateDate = 0; loadFeaturedDate[0] = 0; @@ -7155,7 +7204,7 @@ public class MediaDataController extends BaseController { checkFeaturedStickers(); checkFeaturedEmoji(); checkReactions(); - checkMenuBots(); + checkMenuBots(true); checkPremiumPromo(); checkPremiumGiftStickers(); checkGenericAnimations(); @@ -7788,13 +7837,13 @@ public class MediaDataController extends BaseController { 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)); } @@ -7833,10 +7882,10 @@ public class MediaDataController extends BaseController { 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 b0fde1f40..b55530625 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java @@ -41,6 +41,7 @@ import org.telegram.ui.ActionBar.Theme; 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; @@ -173,6 +174,7 @@ public class MessageObject { public StringBuilder botButtonsLayout; public boolean isRestrictedMessage; public long loadedFileSize; + public boolean forceExpired; public boolean isSpoilersRevealed; public boolean isMediaSpoilersRevealed; @@ -231,6 +233,7 @@ public class MessageObject { public int lastLineWidth; public int textWidth; public int textHeight; + public int captionHeight; public boolean hasRtl; public float textXOffset; @@ -403,7 +406,7 @@ public class MessageObject { } public boolean hasMediaSpoilers() { - return messageOwner.media != null && messageOwner.media.spoiler; + return messageOwner.media != null && messageOwner.media.spoiler || needDrawBluredPreview(); } public boolean shouldDrawReactionsInLayout() { @@ -3593,8 +3596,11 @@ public class MessageObject { 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); @@ -3960,7 +3966,7 @@ public class MessageObject { } 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 { @@ -4076,7 +4082,7 @@ public class MessageObject { } } 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) { @@ -4858,6 +4864,28 @@ public class MessageObject { 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; @@ -6275,10 +6303,23 @@ public class MessageObject { 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) { @@ -6286,7 +6327,14 @@ public class MessageObject { } 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() { @@ -6843,6 +6891,11 @@ public class MessageObject { } 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 9de875bf8..604202fff 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.EditWidgetActivity; 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; @@ -141,6 +142,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; @@ -489,6 +491,7 @@ public class MessagesController extends BaseController implements NotificationCe public int ringtoneDurationMax; public int ringtoneSizeMax; public boolean storiesExportNopublicLink; + public int authorizationAutoconfirmPeriod; public int channelsLimitDefault; public int channelsLimitPremium; @@ -1382,6 +1385,7 @@ public class MessagesController extends BaseController implements NotificationCe 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"); @@ -3319,6 +3323,7 @@ public class MessagesController extends BaseController implements NotificationCe changed = storiesChanged = true; } } + break; } case "stories_entities": { if (value.value instanceof TLRPC.TL_jsonString) { @@ -3329,6 +3334,7 @@ public class MessagesController extends BaseController implements NotificationCe changed = true; } } + break; } case "stories_export_nopublic_link": { if (value.value instanceof TLRPC.TL_jsonBool) { @@ -3339,6 +3345,7 @@ public class MessagesController extends BaseController implements NotificationCe changed = true; } } + break; } case "stories_venue_search_username": { if (value.value instanceof TLRPC.TL_jsonString) { @@ -3349,6 +3356,18 @@ public class MessagesController extends BaseController implements NotificationCe 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; } } } @@ -4112,6 +4131,9 @@ public class MessagesController extends BaseController implements NotificationCe if (storiesController != null) { storiesController.cleanup(); } + if (unconfirmedAuthController != null) { + unconfirmedAuthController.cleanup(); + } showFiltersTooltip = false; @@ -5626,8 +5648,22 @@ public class MessagesController extends BaseController implements NotificationCe } } 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(() -> { @@ -5839,7 +5875,7 @@ public class MessagesController extends BaseController implements NotificationCe 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); @@ -10664,7 +10700,7 @@ public class MessagesController extends BaseController implements NotificationCe } 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); @@ -10716,8 +10752,32 @@ public class MessagesController extends BaseController implements NotificationCe } } + 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) { @@ -10731,7 +10791,7 @@ public class MessagesController extends BaseController implements NotificationCe 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); @@ -10746,7 +10806,9 @@ public class MessagesController extends BaseController implements NotificationCe 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; @@ -11485,7 +11547,7 @@ public class MessagesController extends BaseController implements NotificationCe 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 { @@ -14210,6 +14272,7 @@ public class MessagesController extends BaseController implements NotificationCe LongSparseIntArray markAsReadMessagesInbox = null; LongSparseIntArray stillUnreadMessagesCount = null; LongSparseIntArray markAsReadMessagesOutbox = null; + int markContentAsReadMessagesDate = 0; LongSparseArray> markContentAsReadMessages = null; SparseIntArray markAsReadEncrypted = null; LongSparseArray> deletedMessages = null; @@ -14464,6 +14527,7 @@ public class MessagesController extends BaseController implements NotificationCe } } else if (baseUpdate instanceof TLRPC.TL_updateReadMessagesContents) { TLRPC.TL_updateReadMessagesContents update = (TLRPC.TL_updateReadMessagesContents) baseUpdate; + markContentAsReadMessagesDate = update.date; if (markContentAsReadMessages == null) { markContentAsReadMessages = new LongSparseArray<>(); } @@ -14861,6 +14925,11 @@ public class MessagesController extends BaseController implements NotificationCe updatesOnMainThread = new 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) { @@ -15827,6 +15896,8 @@ public class MessagesController extends BaseController implements NotificationCe } getMessagesStorage().updateMutedDialogsFiltersCounters(); } + } else if (baseUpdate instanceof TLRPC.TL_updateNewAuthorization) { + getUnconfirmedAuthController().processUpdate((TLRPC.TL_updateNewAuthorization) baseUpdate); } else if (baseUpdate instanceof TLRPC.TL_updateChannel) { TLRPC.TL_updateChannel update = (TLRPC.TL_updateChannel) baseUpdate; TLRPC.Dialog dialog = dialogs_dict.get(-update.channel_id); @@ -16591,11 +16662,11 @@ public class MessagesController extends BaseController implements NotificationCe getTopicsController().updateReadOutbox(topicsReadOutbox); } if (markContentAsReadMessages != null) { - int time = getConnectionsManager().getCurrentTime(); + int currentTime2 = getConnectionsManager().getCurrentTime(); for (int a = 0, size = markContentAsReadMessages.size(); a < size; a++) { long key = markContentAsReadMessages.keyAt(a); ArrayList arrayList = markContentAsReadMessages.valueAt(a); - getMessagesStorage().markMessagesContentAsRead(key, arrayList, time); + getMessagesStorage().markMessagesContentAsRead(key, arrayList, currentTime2, markContentAsReadMessagesDate); } } if (deletedMessages != null) { @@ -18372,6 +18443,19 @@ public class MessagesController extends BaseController implements NotificationCe return storiesController; } + public UnconfirmedAuthController getUnconfirmedAuthController() { + if (unconfirmedAuthController != null) { + return unconfirmedAuthController; + } + synchronized (lockObjects[currentAccount]) { + if (unconfirmedAuthController != null) { + return unconfirmedAuthController; + } + unconfirmedAuthController = new UnconfirmedAuthController(currentAccount); + } + return unconfirmedAuthController; + } + public boolean storiesEnabled() { switch (storiesPosting) { case "premium": diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java index b9b1a4224..6ffdb36da 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.SpannableStringBuilder; 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; @@ -95,7 +96,7 @@ public class MessagesStorage extends BaseController { } } - 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(); @@ -606,6 +607,8 @@ public class MessagesStorage extends BaseController { 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(); @@ -672,16 +675,17 @@ public class MessagesStorage extends BaseController { 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(); } @@ -1033,6 +1037,7 @@ public class MessagesStorage extends BaseController { AndroidUtilities.runOnUIThread(() -> getMessagesController().markMessageAsRead2(-channelId, mid, inputChannel, ttl, taskId)); break; } + case 101: case 23: { TLRPC.InputChannel inputChannel; long dialogId = data.readInt64(false); @@ -1043,7 +1048,7 @@ public class MessagesStorage extends BaseController { } 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: @@ -1126,6 +1131,12 @@ public class MessagesStorage extends BaseController { 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(); } @@ -1319,7 +1330,6 @@ public class MessagesStorage extends BaseController { 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"); @@ -5024,7 +5034,7 @@ public class MessagesStorage extends BaseController { AndroidUtilities.runOnUIThread(() -> { if (!inner) { - markMessagesContentAsRead(dialogId, midsArray, 0); + markMessagesContentAsRead(dialogId, midsArray, 0, 0); } getNotificationCenter().postNotificationName(NotificationCenter.messagesReadContent, dialogId, midsArray); }); @@ -5056,6 +5066,59 @@ public class MessagesStorage extends BaseController { }); } + 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; @@ -5099,7 +5162,7 @@ public class MessagesStorage extends BaseController { if (random_ids != null) { AndroidUtilities.runOnUIThread(() -> { - markMessagesContentAsRead(dialogId, midsArray, 0); + markMessagesContentAsRead(dialogId, midsArray, 0, 0); getNotificationCenter().postNotificationName(NotificationCenter.messagesReadContent, dialogId, midsArray); }); } @@ -9769,10 +9832,10 @@ public class MessagesStorage extends BaseController { 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; } @@ -12061,7 +12124,7 @@ public class MessagesStorage extends BaseController { } } - public void markMessagesContentAsRead(long dialogId, ArrayList mids, int date) { + public void markMessagesContentAsRead(long dialogId, ArrayList mids, int currentDate, int readDate) { if (isEmpty(mids)) { return; } @@ -12070,21 +12133,39 @@ public class MessagesStorage extends BaseController { 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); @@ -12094,7 +12175,7 @@ public class MessagesStorage extends BaseController { } } } else { - markMessagesContentAsReadInternal(dialogId, mids, date); + markMessagesContentAsReadInternal(dialogId, mids, currentDate); } }); } @@ -15347,6 +15428,17 @@ public class MessagesStorage extends BaseController { 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 205156cf4..6e895ac7b 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NativeLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NativeLoader.java @@ -22,7 +22,7 @@ import java.util.zip.ZipFile; 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"; @@ -125,7 +125,7 @@ public class NativeLoader { return; } catch (Error e) { FileLog.e(e); - log.append("129: ").append(e).append("\n"); + log.append("128: ").append(e).append("\n"); } String folder = getAbiFolder(); @@ -173,7 +173,7 @@ public class NativeLoader { } } catch (Throwable e) { e.printStackTrace(); - log.append("177: ").append(e).append("\n"); + log.append("176: ").append(e).append("\n"); } try { @@ -181,7 +181,7 @@ public class NativeLoader { 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 e297dc3bc..b304dde64 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java @@ -214,6 +214,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 1c27888a3..9ef27e6d1 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java @@ -934,8 +934,6 @@ public class NotificationsController extends BaseController { 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 de8d646dc..455097950 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java @@ -7808,7 +7808,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe 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; @@ -8481,7 +8481,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe 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 5cbfdeb02..9698bbf1b 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java @@ -144,6 +144,14 @@ public class SharedConfig { .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 { @@ -237,6 +245,7 @@ public class SharedConfig { 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; @@ -615,6 +624,9 @@ public class SharedConfig { 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); @@ -1681,4 +1693,20 @@ public class SharedConfig { } 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 000000000..e3451f101 --- /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 de2cce6f3..da6966fb0 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.math.BigInteger; 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; @@ -516,10 +518,22 @@ public class Utilities { 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); } @@ -531,6 +545,9 @@ public class Utilities { 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); @@ -576,4 +593,9 @@ public class Utilities { } 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 0eb03d41f..a329c5122 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.graphics.Canvas; 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.AnimatedFileDrawable; 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 class VideoEditedInfo { 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 class VideoEditedInfo { 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 class VideoEditedInfo { public float density; public int W, H; + public ReactionsLayoutInBubble.VisibleReaction visibleReaction; public MediaEntity() { @@ -207,6 +214,9 @@ public class VideoEditedInfo { } } } + 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 class VideoEditedInfo { data.writeInt32(TLRPC.TL_null.constructor); } } + if (type == TYPE_REACTION) { + mediaArea.serializeToStream(data); + } } public MediaEntity copy() { @@ -290,7 +303,7 @@ public class VideoEditedInfo { 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 class VideoEditedInfo { } 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 class VideoEditedInfo { } 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 class VideoEditedInfo { 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 class VideoEditedInfo { 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 000000000..0af9045f5 --- /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 000000000..64a891d9f --- /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 000000000..6247c6fea --- /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 f5c4b4e9f..931f4281d 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 @@ package org.telegram.messenger.video; 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; - } + 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); - ByteBuffer decoderInputBuffer; - if (android.os.Build.VERSION.SDK_INT >= 21) { - decoderInputBuffer = decoder.getInputBuffer(decoderInputBufferIndex); - } else { - decoderInputBuffer = decoderInputBuffers[decoderInputBufferIndex]; + 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; + } } - 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 (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 class AudioRecoder { 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 cec8e3a6c..f6e5495f2 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 @@ package org.telegram.messenger.video; 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.R; 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 class MediaCodecVideoConvertor { } @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 @@ public class MediaCodecVideoConvertor { int prependHeaderSize = 0; endPresentationTime = duration * 1000; checkConversionCanceled(); + AudioRecoder audioRecoder = null; if (isPhoto) { try { @@ -180,7 +178,7 @@ public class MediaCodecVideoConvertor { 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 @@ public class MediaCodecVideoConvertor { 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 @@ public class MediaCodecVideoConvertor { encoder.release(); encoder = null; } + if (audioRecoder != null) { + audioRecoder.release(); + } checkConversionCanceled(); } else { extractor = new MediaExtractor(); @@ -343,7 +362,6 @@ public class MediaCodecVideoConvertor { } if (needCompress || needConvertVideo) { - AudioRecoder audioRecoder = null; ByteBuffer audioBuffer = null; boolean copyAudioBuffer = true; long lastFramePts = -1; @@ -439,7 +457,7 @@ public class MediaCodecVideoConvertor { // 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 @@ public class MediaCodecVideoConvertor { 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 @@ public class MediaCodecVideoConvertor { } 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 @@ public class MediaCodecVideoConvertor { 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 @@ public class MediaCodecVideoConvertor { extractor.seekTo(0, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); } } else { - MediaExtractor audioExtractor = new MediaExtractor(); - audioExtractor.setDataSource(videoPath); - audioExtractor.selectTrack(audioIndex); - - if (startTime > 0) { - audioExtractor.seekTo(startTime, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); - } else { - audioExtractor.seekTo(0, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); + ArrayList audioInputs = new ArrayList<>(); + GeneralAudioInput mainInput = new GeneralAudioInput(videoPath, audioIndex); + if (endTime > 0) { + mainInput.setEndTimeUs(endTime); } + if (startTime > 0) { + 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 @@ public class MediaCodecVideoConvertor { while (!outputDone || (!copyAudioBuffer && !audioEncoderDone)) { checkConversionCanceled(); - if (!copyAudioBuffer && audioRecoder != null) { + if (audioRecoder != null) { audioEncoderDone = audioRecoder.step(mediaMuxer, audioTrackIndex); } @@ -879,43 +905,39 @@ public class MediaCodecVideoConvertor { 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 @@ public class MediaCodecVideoConvertor { 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 @@ public class MediaCodecVideoConvertor { 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 @@ public class MediaCodecVideoConvertor { } } + public class ConversionCanceledException extends RuntimeException { public ConversionCanceledException() { @@ -1273,4 +1315,104 @@ public class MediaCodecVideoConvertor { 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 6c65b9b9a..6a9c203c3 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 f96214372..4989e1420 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.tgnet.TLRPC; 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 class 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 class 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 @@ public class TextureRenderer { } public void drawFrame(SurfaceTexture st) { + boolean blurred = false; if (isPhoto) { drawGradient(); } else { @@ -487,7 +505,7 @@ public class TextureRenderer { 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 class TextureRenderer { 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 class TextureRenderer { 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 class TextureRenderer { } 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 class TextureRenderer { 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 class TextureRenderer { } 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 class TextureRenderer { 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 5e30a3317..9bf10baca 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 class VideoPlayerHolderBase { 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 000000000..6b59e3e7a --- /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 000000000..8ac395738 --- /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 000000000..7f4e51e5b --- /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 000000000..3200e7f5c --- /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 000000000..9f20f3329 --- /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 000000000..26062c547 --- /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 000000000..4617002db --- /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 000000000..f77c9cd5b --- /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 000000000..54f7ac35f --- /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 000000000..fbb8f1cad --- /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 000000000..2dda9634c --- /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 000000000..beee8cabe --- /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 000000000..c3eb185f5 --- /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 faac63606..1d5c8f83e 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java @@ -74,7 +74,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; @@ -5691,6 +5691,7 @@ public class TLRPC { 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; @@ -5724,6 +5725,7 @@ public class TLRPC { 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); @@ -5745,6 +5747,7 @@ public class TLRPC { 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); @@ -6525,6 +6528,9 @@ public class TLRPC { 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; @@ -6559,6 +6565,9 @@ public class TLRPC { 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) { @@ -6593,6 +6602,9 @@ public class TLRPC { 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) { @@ -12495,7 +12507,7 @@ public class TLRPC { } } } - + public static class WebPageAttribute extends TLObject { public int flags; @@ -12521,7 +12533,7 @@ public class TLRPC { public static class TL_webPageAttributeStory extends WebPageAttribute { public static final int constructor = 0x939a4671; - + public long user_id; public int id; public TLRPC.StoryItem storyItem; @@ -21120,6 +21132,7 @@ public class TLRPC { 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; @@ -32931,6 +32944,9 @@ public class TLRPC { case 0x1b49ec6d: result = new TL_updateDraftMessage(); break; + case 0x8951abef: + result = new TL_updateNewAuthorization(); + break; case 0xa7848924: result = new TL_updateUserName(); break; @@ -33051,7 +33067,7 @@ public class TLRPC { case 0x922e6e10: result = new TL_updateReadChannelInbox(); break; - case 0x68c13933: + case 0xf8227181: result = new TL_updateReadMessagesContents(); break; case 0x7761198: @@ -34250,7 +34266,7 @@ public class TLRPC { public long user_id; public EmojiStatus emoji_status; - + public void readParams(AbstractSerializedData stream, boolean exception) { user_id = stream.readInt64(exception); emoji_status = EmojiStatus.TLdeserialize(stream, stream.readInt32(exception), exception); @@ -34458,6 +34474,28 @@ public class TLRPC { } } + 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; @@ -35060,7 +35098,7 @@ public class TLRPC { stream.writeInt32(constructor); } } - + public static class TL_updateRecentEmojiStatuses extends Update { public static int constructor = 0x30f443db; @@ -35310,13 +35348,16 @@ public class TLRPC { } 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) { @@ -35330,10 +35371,14 @@ public class TLRPC { } 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); @@ -35342,6 +35387,9 @@ public class TLRPC { } stream.writeInt32(pts); stream.writeInt32(pts_count); + if ((flags & 1) != 0) { + stream.writeInt32(date); + } } } @@ -48590,7 +48638,7 @@ public class TLRPC { } public static abstract class ReactionCount extends TLObject { - + public int flags; public int chosen_order; public boolean chosen; //custom @@ -53277,6 +53325,7 @@ public class TLRPC { public static int constructor = 0x40f48462; public int flags; + public boolean confirmed; public long hash; public boolean encrypted_requests_disabled; public boolean call_requests_disabled; @@ -53287,6 +53336,7 @@ public class TLRPC { public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); + flags = confirmed ? (flags | 8) : (flags &~ 8); stream.writeInt32(flags); stream.writeInt64(hash); if ((flags & 1) != 0) { @@ -65936,18 +65986,9 @@ public class TLRPC { } } - public static class TL_attachMenuBot extends AttachMenuBot { + public static class TL_attachMenuBot_layer162 extends TL_attachMenuBot { public static int constructor = 0xc8aa2cd2; - public int flags; - public boolean inactive; - public boolean has_settings; - public boolean request_write_access; - public long bot_id; - public String short_name; - public ArrayList peer_types = new ArrayList<>(); - public ArrayList icons = new ArrayList<>(); - public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); inactive = (flags & 1) != 0; @@ -66010,16 +66051,102 @@ public class TLRPC { } } + 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<>(); + public ArrayList icons = new ArrayList<>(); + + 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; + 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); + 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); + 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); + 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 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) { @@ -66552,12 +66679,14 @@ public class TLRPC { } 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; @@ -66568,9 +66697,15 @@ public class TLRPC { 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); } @@ -68696,10 +68831,10 @@ public class TLRPC { stream.writeBool(enabled); } } - + public static class TL_channels_clickSponsoredMessage extends TLObject { public static int constructor = 0x18afbc93; - + public InputChannel channel; public byte[] random_id; @@ -69686,10 +69821,10 @@ public class TLRPC { 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; @@ -69728,7 +69863,7 @@ public class TLRPC { stream.writeDouble(rotation); } } - + public static class MediaArea extends TLObject { public TL_mediaAreaCoordinates coordinates; @@ -69754,10 +69889,10 @@ public class TLRPC { return result; } } - + public static class TL_mediaAreaVenue extends MediaArea { public static final int constructor = 0xbe82db9c; - + public GeoPoint geo; public String title; public String address; @@ -70170,7 +70305,7 @@ public class TLRPC { stealth_mode.serializeToStream(stream); } } - + public static class TL_stories_canSendStory extends TLObject { public static int constructor = 0xb100d45d; @@ -70382,15 +70517,15 @@ public class TLRPC { 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); } @@ -70702,12 +70837,12 @@ public class TLRPC { 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 5d301f725..8ca2b8905 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java @@ -417,12 +417,7 @@ public class ActionBar extends FrameLayout { 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 ad5365bbd..ab2774aa6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java @@ -490,7 +490,8 @@ public class ActionBarLayout extends FrameLayout implements INavigationLayout, F @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 faa83afb4..c7a3fc70f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java @@ -346,6 +346,7 @@ public class ActionBarPopupWindow extends PopupWindow { } } + @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 d1f4ba8c5..f396ab317 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 class AlertDialog extends Dialog implements Drawable.Callback, Notificati 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 class AlertDialog extends Dialog implements Drawable.Callback, Notificati 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 @@ public class AlertDialog extends Dialog implements Drawable.Callback, Notificati } } containerView.setFitsSystemWindows(Build.VERSION.SDK_INT >= 21); - setContentView(containerView); + if (setContent) { + setContentView(containerView); + } final boolean hasButtons = positiveButtonText != null || negativeButtonText != null || neutralButtonText != null; @@ -1111,6 +1110,13 @@ public class AlertDialog extends Dialog implements Drawable.Callback, Notificati window.setAttributes(params); + return containerView; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + inflateContent(true); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded); } @@ -1464,7 +1470,11 @@ public class AlertDialog extends Dialog implements Drawable.Callback, Notificati } 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 000000000..cbabb6538 --- /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 65566b2be..09bac8032 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java @@ -123,6 +123,7 @@ public class BottomSheet extends Dialog { protected int behindKeyboardColor; private boolean canDismissWithSwipe = true; + private boolean canDismissWithTouchOutside = true; private boolean allowCustomAnimation = true; private boolean showWithoutAnimation; @@ -980,6 +981,23 @@ public class BottomSheet extends Dialog { 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; @@ -1321,7 +1339,11 @@ public class BottomSheet extends Dialog { } 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 16d898761..ac1671763 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.BitmapDrawable; 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 d3bd4acc3..836eb15ab 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 class EmojiThemes { } } - 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 class EmojiThemes { } - 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 class EmojiThemes { 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 class EmojiThemes { 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 class EmojiThemes { 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 class EmojiThemes { } 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 class EmojiThemes { 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 class EmojiThemes { } 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 f4eb6887b..c121b9648 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 @@ import org.telegram.messenger.AndroidUtilities; public class RoundVideoShadow extends Drawable { - Paint eraserPaint; Paint paint; public RoundVideoShadow() { -// eraserPaint = new Paint(Paint.ANTI_ALIAS_FLAG); -// eraserPaint.setColor(0); -// eraserPaint.setStyle(Paint.Style.FILL); -// eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); - paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setShadowLayer(AndroidUtilities.dp(4), 0, 0, 0x5f000000); } @@ -32,9 +26,6 @@ public class RoundVideoShadow extends Drawable { 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 class RoundVideoShadow extends Drawable { @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 84b2dff5e..a5663eb17 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java @@ -99,6 +99,7 @@ import org.telegram.messenger.time.SunDate; 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; @@ -3112,6 +3113,7 @@ public class Theme { 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; @@ -6226,6 +6228,7 @@ public class Theme { } catch (Exception e) { FileLog.e(e); } + BlurSettingsBottomSheet.onThemeApplyed(); if (previousTheme == null && save && !switchingNightTheme) { MessagesController.getInstance(themeInfo.account).saveTheme(themeInfo, themeInfo.getAccent(false), nightTheme, false); } @@ -8301,12 +8304,16 @@ public class Theme { 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); @@ -8538,7 +8545,7 @@ public class Theme { 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); @@ -8618,6 +8625,7 @@ public class Theme { 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)); @@ -8700,7 +8708,6 @@ public class Theme { 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 d8ab63133..eda95947e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeColors.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeColors.java @@ -47,7 +47,7 @@ public class ThemeColors { 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; @@ -56,7 +56,7 @@ public class ThemeColors { 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 cbbd484d8..8a40aa73a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/ContactsAdapter.java @@ -22,6 +22,7 @@ import androidx.recyclerview.widget.RecyclerView; 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 c45120211..fe12b540f 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 class DialogsAdapter extends RecyclerListView.SelectionAdapter implements } 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 2527da4eb..06c3d0ab4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DrawerLayoutAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DrawerLayoutAdapter.java @@ -18,6 +18,7 @@ import androidx.recyclerview.widget.RecyclerView; 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.R; import org.telegram.messenger.UserConfig; @@ -309,17 +310,31 @@ public class DrawerLayoutAdapter extends RecyclerListView.SelectionAdapter { 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 } items.add(new Item(2, LocaleController.getString("NewGroup", R.string.NewGroup), newGroupIcon)); @@ -363,10 +378,23 @@ public class DrawerLayoutAdapter extends RecyclerListView.SelectionAdapter { return 1 + accountNumbers.size(); } + 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; @@ -374,8 +402,17 @@ public class DrawerLayoutAdapter extends RecyclerListView.SelectionAdapter { 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 d2d085150..7bce10d03 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java @@ -1773,7 +1773,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement 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 0bbf39000..64d2ecbc7 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 class ArchiveSettingsActivity extends BaseFragment implements Notificatio @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 ecd768c23..3057998ab 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.KeyEvent; 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; @@ -147,6 +149,7 @@ import org.telegram.ui.Cells.TextSelectionHelper; 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; @@ -11646,6 +11649,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg 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(); @@ -11663,6 +11667,14 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg } } } + 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 4f1e4415c..88ed91877 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.LinearLayout; 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 @@ public class BlurSettingsBottomSheet extends BottomSheet { 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 class BlurSettingsBottomSheet extends BottomSheet { public void onSeekBarDrag(boolean stop, float progress) { alphaTextView.setText("Alpha " + blurAlpha); blurAlpha = progress; - fragment.contentView.invalidateBlur(); + contentView.invalidateBlur(); } @Override @@ -108,13 +116,13 @@ public class BlurSettingsBottomSheet extends BottomSheet { @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 class BlurSettingsBottomSheet extends BottomSheet { 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 ba1487ce6..264f32573 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 @@ public class BubbleActivity extends BasePermissionsActivity implements INavigati 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 bdb9b620a..6052f9af4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CacheControlActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CacheControlActivity.java @@ -49,8 +49,6 @@ import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.google.android.exoplayer2.util.Log; - import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.BotWebViewVibrationEffect; import org.telegram.messenger.BuildVars; @@ -1140,7 +1138,7 @@ public class CacheControlActivity extends BaseFragment implements NotificationCe 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 af8276a01..5181b1b87 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CalendarActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CalendarActivity.java @@ -163,7 +163,7 @@ public class CalendarActivity extends BaseFragment implements NotificationCenter 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 c98472ef8..fd20b2d43 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java @@ -1085,9 +1085,9 @@ public class ChatActionCell extends BaseCell implements DownloadController.FileD } 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 c1dd40712..98fe8c683 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.Bitmap; 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.URLSpanNoUnderline; 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; @@ -194,6 +197,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate int parentBoundsBottom; public ExpiredStoryView expiredStoryView; + private boolean skipFrameUpdate; public RadialProgress2 getRadialProgress() { return radialProgress; @@ -378,7 +382,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate currentMessageObject.markReactionsAsRead(); } - public void setVisibleOnScreen(boolean visibleOnScreen) { + public void setVisibleOnScreen(boolean visibleOnScreen, float clipTop, float clipBottom) { if (this.visibleOnScreen != visibleOnScreen) { this.visibleOnScreen = visibleOnScreen; checkImageReceiversAttachState(); @@ -386,6 +390,16 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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) { @@ -718,6 +732,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate private boolean drawRadialCheckBackground; private ImageReceiver photoImage; private ImageReceiver blurredPhotoImage; + private ColorMatrixColorFilter fancyBlurFilter; private AvatarDrawable contactAvatarDrawable; private Drawable locationLoadingThumb; private Drawable gradientDrawable; @@ -950,6 +965,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate private ArrayList lastPollResults; private int lastPollResultsVoters; private TimerParticles timerParticles; + private AnimatedFloat timerParticlesAlpha; private int pollHintX; private int pollHintY; private boolean pollHintPressed; @@ -1146,6 +1162,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate private float mediaSpoilerRevealX; private float mediaSpoilerRevealY; private float mediaSpoilerRevealMaxRadius; + private SpoilerEffect2 mediaSpoilerEffect2; private float unlockAlpha = 1f; private float unlockX; @@ -3092,7 +3109,11 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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) { @@ -3253,7 +3274,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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) { @@ -3597,7 +3618,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } private void didClickedImage() { - if (currentMessageObject.hasMediaSpoilers() && !currentMessageObject.isMediaSpoilersRevealed) { + if (currentMessageObject.hasMediaSpoilers() && !currentMessageObject.needDrawBluredPreview() && !currentMessageObject.isMediaSpoilersRevealed) { startRevealMedia(lastTouchX, lastTouchY); return; } @@ -3694,13 +3715,18 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (messageObject == null || !messageObject.needDrawBluredPreview()) { 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(); } @@ -3879,6 +3905,10 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate return photoImage; } + public ImageReceiver getBlurredPhotoImage() { + return blurredPhotoImage; + } + public int getNoSoundIconCenterX() { return noSoundCenterX; } @@ -3961,6 +3991,10 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (currentNameStatusDrawable != null) { currentNameStatusDrawable.detach(); } + + if (mediaSpoilerEffect2 != null) { + mediaSpoilerEffect2.detach(this); + } } @Override @@ -4039,87 +4073,103 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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 { + 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(); + } + } + 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 { - updateButtonState(false, false, false); + photoImage.setFileLoadingPriority(FileLoader.PRIORITY_LOW); + cancelLoading(currentMessageObject); } - 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); - } - } - 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(); - } - } - replyImageReceiver.onDetachedFromWindow(); - locationImageReceiver.onDetachedFromWindow(); - photoImage.onDetachedFromWindow(); - blurredPhotoImage.onDetachedFromWindow(); - - cancelLoading(currentMessageObject); - AnimatedEmojiSpan.release(this, animatedEmojiDescriptionStack); - AnimatedEmojiSpan.release(this, animatedEmojiReplyStack); - AnimatedEmojiSpan.release(this, animatedEmojiStack); } } @@ -5669,6 +5719,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } if (photoImage.getBitmap() != null && !photoImage.getBitmap().isRecycled() && currentMessageObject.hasMediaSpoilers() && !currentMessageObject.isMediaSpoilersRevealed) { blurredPhotoImage.setImageBitmap(Utilities.stackBlurBitmapMax(photoImage.getBitmap())); + blurredPhotoImage.setColorFilter(getFancyBlurFilter()); } drawPhotoImage = true; @@ -6801,6 +6852,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } 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()); @@ -7001,10 +7053,11 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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; @@ -7061,7 +7114,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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); @@ -7152,7 +7205,6 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } 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) { @@ -7170,6 +7222,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } else { captionWidth = widthForCaption; } + updateCaptionSpoilers(); if (widthCaptionMin > 0 && captionWidth > widthCaptionMin) { photoWidth += captionWidth - widthCaptionMin; backgroundWidth += captionWidth - widthCaptionMin; @@ -7189,6 +7242,8 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate captionLayout = null; updateCaptionSpoilers(); } + } else { + updateCaptionSpoilers(); } } catch (Exception e) { FileLog.e(e); @@ -7268,6 +7323,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate messageObject.type == MessageObject.TYPE_ROUND_VIDEO ) { if (messageObject.needDrawBluredPreview()) { + photoImage.setColorFilter(getFancyBlurFilter()); currentPhotoFilter += "_b2"; currentPhotoFilterThumb += "_b2"; } else { @@ -7286,6 +7342,8 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate currentPhotoFilter = filterNew; } } + } else if (messageObject.needDrawBluredPreview()) { + photoImage.setColorFilter(getFancyBlurFilter()); } } @@ -7434,6 +7492,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } if (photoImage.getBitmap() != null && !photoImage.getBitmap().isRecycled() && currentMessageObject.hasMediaSpoilers() && !currentMessageObject.isMediaSpoilersRevealed) { blurredPhotoImage.setImageBitmap(Utilities.stackBlurBitmapMax(photoImage.getBitmap())); + blurredPhotoImage.setColorFilter(getFancyBlurFilter()); } setMessageObjectInternal(messageObject); @@ -7506,6 +7565,16 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } 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(); } @@ -7955,6 +8024,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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; @@ -9581,6 +9653,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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; @@ -9753,7 +9826,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate photoImageOutOfBounds = true; } if (!photoImageOutOfBounds || drawForBlur) { - photoImage.setSkipUpdateFrame(drawForBlur); + photoImage.setSkipUpdateFrame(drawForBlur || skipFrameUpdate); if (flipImage) { canvas.save(); canvas.scale(-1f, 1, photoImage.getCenterX(), photoImage.getCenterY()); @@ -9776,7 +9849,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate drawBlurredPhoto(canvas); } } - photoImage.setSkipUpdateFrame(false); + photoImage.setSkipUpdateFrame(skipFrameUpdate); } } if (isRoundVideo && currentMessageObject.isVoiceTranscriptionOpen() && pipFloat > 0) { @@ -10379,17 +10452,26 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate canvas.clipPath(mediaSpoilerPath, Region.Op.DIFFERENCE); } - blurredPhotoImage.setImageCoords(photoImage.getImageX(), photoImage.getImageY(), photoImage.getImageWidth(), photoImage.getImageHeight()); - blurredPhotoImage.setRoundRadius(photoImage.getRoundRadius()); - blurredPhotoImage.draw(canvas); + if (currentMessageObject.needDrawBluredPreview()) { + photoImage.draw(canvas); + } else { + 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(); - - 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() { @@ -11355,15 +11437,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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; } @@ -12190,6 +12264,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } if (currentMessageObject.hasMediaSpoilers() && imageReceiver.getBitmap() != null && !imageReceiver.getBitmap().isRecycled()) { blurredPhotoImage.setImageBitmap(Utilities.stackBlurBitmapMax(imageReceiver.getBitmap())); + blurredPhotoImage.setColorFilter(getFancyBlurFilter()); } } } @@ -13384,7 +13459,6 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (!wasLayout) { onLayout(false, getLeft(), getTop(), getRight(), getBottom()); } - if (enterTransitionInProgress && currentMessageObject.isAnimatedEmojiStickers()) { return; } @@ -16119,13 +16193,31 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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); @@ -16958,7 +17050,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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(); } @@ -17014,17 +17106,31 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } 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)) { @@ -17694,17 +17800,35 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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; } } @@ -17712,17 +17836,23 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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()) { @@ -19739,4 +19869,13 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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 2c60ee7e4..666ac3bc1 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.CheckBox2; 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 class CheckBoxCell extends FrameLayout { 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 class CheckBoxCell extends FrameLayout { 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 @@ public class CheckBoxCell extends FrameLayout { 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 @@ public class CheckBoxCell extends FrameLayout { } 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 211d11732..c7ca3625e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java @@ -1444,7 +1444,7 @@ public class DialogCell extends BaseCell implements StoriesListPlaceProvider.Ava } } 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(); @@ -1545,7 +1545,7 @@ public class DialogCell extends BaseCell implements StoriesListPlaceProvider.Ava } } 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; @@ -1581,7 +1581,7 @@ public class DialogCell extends BaseCell implements StoriesListPlaceProvider.Ava } 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); @@ -3830,7 +3830,9 @@ public class DialogCell extends BaseCell implements StoriesListPlaceProvider.Ava } if (avatarImage.getVisible()) { - needInvalidate = drawAvatarOverlays(canvas); + if (drawAvatarOverlays(canvas)) { + needInvalidate = true; + } } if (rightFragmentOpenedProgress > 0 && currentDialogFolderId == 0) { @@ -4010,6 +4012,8 @@ public class DialogCell extends BaseCell implements StoriesListPlaceProvider.Ava 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 f18798dae..1a746316b 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 android.widget.TextView; 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 class DrawerActionCell extends FrameLayout { } } - public ImageView getImageView() { + public BackupImageView getImageView() { return imageView; } @@ -124,4 +134,32 @@ public class DrawerActionCell extends FrameLayout { 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 98b945faf..33f62feff 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java @@ -358,7 +358,7 @@ public class DrawerProfileCell extends FrameLayout implements NotificationCenter (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 be86c0f40..93c17c673 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.CheckBox2; 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 class PhotoAttachPhotoCell extends FrameLayout { 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 @@ public class PhotoAttachPhotoCell extends FrameLayout { } 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 @@ public class PhotoAttachPhotoCell extends FrameLayout { 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 @@ public class PhotoAttachPhotoCell extends FrameLayout { 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 class PhotoAttachPhotoCell extends FrameLayout { 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 0cb677bbb..e2da8e14b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java @@ -752,6 +752,8 @@ public class ProfileSearchCell extends BaseCell implements NotificationCenter.No } 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 d64114e93..5f5c4d905 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.MessageSeenCheckDrawable; 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 class ReactedUserHolderView extends FrameLayout { 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 class ReactedUserHolderView extends FrameLayout { 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 class ReactedUserHolderView extends FrameLayout { @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 b9337b333..8ca76b8cf 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.Log; 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.CombinedDrawable; 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 class SharedPhotoVideoCell2 extends View { 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 class SharedPhotoVideoCell2 extends View { blurImageReceiver.setImageBitmap(Utilities.stackBlurBitmapMax(imageReceiver.getBitmap())); } }); + + setWillNotDraw(false); } public void setStyle(int style) { @@ -152,6 +158,7 @@ public class SharedPhotoVideoCell2 extends View { } currentMessageObject = messageObject; isStory = currentMessageObject != null && currentMessageObject.isStory(); + updateSpoilers2(); if (messageObject == null) { imageReceiver.onDetachedFromWindow(); blurImageReceiver.onDetachedFromWindow(); @@ -385,10 +392,15 @@ public class SharedPhotoVideoCell2 extends View { 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 @@ public class SharedPhotoVideoCell2 extends View { imageReceiver.onAttachedToWindow(); blurImageReceiver.onAttachedToWindow(); } + if (mediaSpoilerEffect2 != null) { + if (mediaSpoilerEffect2.destroyed) { + mediaSpoilerEffect2 = SpoilerEffect2.getInstance(this); + } else { + mediaSpoilerEffect2.attach(this); + } + } } @Override @@ -524,6 +543,9 @@ public class SharedPhotoVideoCell2 extends View { imageReceiver.onDetachedFromWindow(); blurImageReceiver.onDetachedFromWindow(); } + if (mediaSpoilerEffect2 != null) { + mediaSpoilerEffect2.detach(this); + } } public void setGradientView(FlickerLoadingView globalGradientView) { @@ -538,6 +560,23 @@ public class SharedPhotoVideoCell2 extends View { 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 5c0ee74d6..73cf4d61a 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 class TextCheckCell2 extends FrameLayout { 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 class TextCheckCell2 extends FrameLayout { 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 000000000..58325f4af --- /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 1ad1a8867..b2d3232dd 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChannelCreateActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChannelCreateActivity.java @@ -1375,7 +1375,7 @@ public class ChannelCreateActivity extends BaseFragment implements NotificationC 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 55f781cce..5de5978a6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java @@ -113,6 +113,7 @@ import androidx.viewpager.widget.PagerAdapter; 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.telegram.PhoneFormat.PhoneFormat; @@ -2047,6 +2048,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not public boolean hasForwardingMessages() { return forwardingMessages != null && !forwardingMessages.messages.isEmpty(); } + + @Override + public void onKeyboardRequested() { + checkAdjustResize(); + } } private final ChatScrollCallback chatScrollHelperCallback = new ChatScrollCallback(); @@ -3149,6 +3155,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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) { @@ -5513,6 +5521,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } if (dy != 0) { + contentView.invalidateBlur(); hideHints(true); } if (dy != 0 && scrollingFloatingDate && !currentFloatingTopIsNotMessage) { @@ -6435,6 +6444,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } }; + chatActivityEnterView.getEditField().adaptiveCreateLinkDialog = true; chatActivityEnterView.setDelegate(new ChatActivityEnterViewDelegate()); chatActivityEnterView.setDialogId(dialog_id, currentAccount); if (chatInfo != null) { @@ -9977,7 +9987,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -10673,7 +10683,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } pendingLinkSearchString = null; foundUrls = null; - showFieldPanelForWebPage(false, foundWebPage, false); +// showFieldPanelForWebPage(false, foundWebPage, false); } final MessagesController messagesController = getMessagesController(); Utilities.searchQueue.postRunnable(() -> { @@ -10772,11 +10782,15 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } 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; @@ -11397,25 +11411,39 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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(); @@ -11726,6 +11754,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; @@ -11748,18 +11779,18 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; @@ -15658,7 +15689,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not obj.setIsRead(); } } - approximateHeightSum += obj.getApproximateHeight(); + if (approximateHeightSum <= AndroidUtilities.displaySize.y / 2) { + approximateHeightSum += getHeightForMessage(obj); + } if (currentUser != null) { if (currentUser.self) { obj.messageOwner.out = true; @@ -15760,7 +15793,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; @@ -20475,7 +20514,20 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } 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)); @@ -20495,7 +20547,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (error2 == null) { MediaDataController.getInstance(currentAccount).loadAttachMenuBots(false, true); - openAttachBotLayout(user.id, attachMenuBotStartCommand); + openAttachBotLayout(user.id, attachMenuBotStartCommand, false); } }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); }) @@ -20529,10 +20581,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not })); } - 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 @@ -20552,7 +20604,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @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 { @@ -23667,15 +23719,17 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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)); @@ -23806,7 +23860,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -26317,6 +26371,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } 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); @@ -29849,10 +29905,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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())); @@ -31220,7 +31277,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not chatThemeBottomSheet = null; chatListView.setOnInterceptTouchListener(null); setChildrenEnabled(contentView, true); - ChatThemeController.clearWallpaperThumbImages(); + ChatThemeController.getInstance(currentAccount).clearWallpaperThumbImages(); }); } @@ -31253,9 +31310,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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 204eee1c0..bd0f5cadd 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.AndroidUtilities; 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 class ChatBackgroundDrawable extends Drawable { 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 9616bf9e4..361875acc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java @@ -228,6 +228,9 @@ public class ChatEditActivity extends BaseFragment implements ImageUpdater.Image @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; @@ -248,6 +251,7 @@ public class ChatEditActivity extends BaseFragment implements ImageUpdater.Image 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 abe8550db..15b931a21 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditTypeActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditTypeActivity.java @@ -649,7 +649,7 @@ public class ChatEditTypeActivity extends BaseFragment implements NotificationCe 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 867903c31..4808e6422 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 @@ public class ChatReactionsEditActivity extends BaseFragment implements Notificat 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 070a2006a..d34dae86f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatRightsEditActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatRightsEditActivity.java @@ -1020,7 +1020,7 @@ public class ChatRightsEditActivity extends BaseFragment { }), 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 5b110c88d..3e01f71ed 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.DialogInterface; 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.Editable; 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.view.inputmethod.EditorInfo; 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; @@ -114,7 +108,6 @@ import org.telegram.ui.Components.voip.VoIPHelper; 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; @@ -300,7 +293,7 @@ public class AlertsCreator { 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)); @@ -319,7 +312,7 @@ public class AlertsCreator { } 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)); } @@ -332,7 +325,7 @@ public class AlertsCreator { } 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)); } @@ -427,7 +420,7 @@ public class AlertsCreator { 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)); } @@ -2490,7 +2483,7 @@ public class AlertsCreator { 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 a59c35190..ab4b10128 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 class AnimatedEmojiSpan extends ReplacementSpan { } 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 class AnimatedEmojiSpan extends ReplacementSpan { 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 8051053d1..be60a762f 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 android.view.accessibility.AccessibilityNodeInfo; 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 class AnimatedTextView extends View { 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 class AnimatedTextView extends View { 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 838b37249..21f1c2b47 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 class BlurBehindDrawable { } 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 000000000..cc48da38a --- /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 605ad68c7..0541f0a49 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.List; 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 class BotWebViewContainer extends FrameLayout implements NotificationCent 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 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent } 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 class BotWebViewContainer extends FrameLayout implements NotificationCent @SuppressWarnings("deprecation") public void evaluateJs(String script, boolean create) { - 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)); + 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)); + } + } + }); } @Override @@ -1442,6 +1449,9 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent 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 @@ public class BotWebViewContainer extends FrameLayout implements NotificationCent 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 f67325aab..599e5c761 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 class BotWebViewMenuContainer extends FrameLayout implements Notification 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 @@ -826,7 +831,12 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification 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 6f8aa4177..2d5ec10e3 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; @@ -29,15 +31,18 @@ import androidx.annotation.NonNull; 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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; @@ -490,6 +530,23 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } 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)) { @@ -780,9 +837,21 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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) { @@ -812,6 +881,8 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi webViewContainer.reload(); } else if (id == R.id.menu_settings) { webViewContainer.onSettingsButtonPressed(); + } else if (id == R.id.menu_delete_bot) { + deleteBot(currentAccount, botId, () -> dismiss()); } } }); @@ -856,8 +927,6 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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); } })); @@ -870,6 +939,7 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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; @@ -882,7 +952,6 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi TLRPC.TL_simpleWebViewResultUrl resultUrl = (TLRPC.TL_simpleWebViewResultUrl) response; queryId = 0; webViewContainer.loadUrl(currentAccount, resultUrl.url); - swipeContainer.setWebView(webViewContainer.getWebView()); } })); break; @@ -913,8 +982,6 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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); } })); @@ -949,7 +1016,6 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi TLRPC.TL_appWebViewResultUrl result = (TLRPC.TL_appWebViewResultUrl) response2; queryId = 0; webViewContainer.loadUrl(currentAccount, result.url); - swipeContainer.setWebView(webViewContainer.getWebView()); AndroidUtilities.runOnUIThread(pollRunnable, POLL_PERIOD); } @@ -958,6 +1024,41 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } } + 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); } @@ -973,11 +1074,15 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 000000000..7b61e26b3 --- /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 84d46bf3c..b18155cc2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BulletinFactory.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BulletinFactory.java @@ -302,18 +302,18 @@ public final class BulletinFactory { 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 000000000..630edd712 --- /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 f48d7c996..1272bd641 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java @@ -55,7 +55,6 @@ import android.text.TextPaint; import android.text.TextUtils; import android.text.TextWatcher; import android.text.style.ImageSpan; -import android.util.Log; import android.util.Property; import android.util.TypedValue; import android.view.ActionMode; @@ -172,6 +171,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; @@ -304,6 +304,10 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific default TLRPC.StoryItem getReplyToStory() { return null; } + + default void onKeyboardRequested() { + + } } public final static int RECORD_STATE_ENTER = 0; @@ -623,6 +627,9 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific } if (!destroyed && messageEditText != null && waitingForKeyboardOpen && !keyboardVisible && !AndroidUtilities.usingHardwareInput && !AndroidUtilities.isInMultiwindow) { + if (delegate != null) { + delegate.onKeyboardRequested(); + } messageEditText.requestFocus(); AndroidUtilities.showKeyboard(messageEditText); AndroidUtilities.cancelRunOnUIThread(openKeyboardRunnable); @@ -5051,6 +5058,9 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific int visibility = getVisibility(); if (showKeyboardOnResume && parentFragment != null && parentFragment.isLastFragment()) { showKeyboardOnResume = false; + if (delegate != null) { + delegate.onKeyboardRequested(); + } if (searchingType == 0 && messageEditText != null) { messageEditText.requestFocus(); } @@ -5193,6 +5203,7 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific } if (overrideHint != null) { messageEditText.setHintText(overrideHint, animated); + messageEditText.setHintText2(overrideHint2, animated); return; } if (!sendPlainEnabled && !isEditingMessage()) { @@ -9402,6 +9413,9 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific return; } showPopup(AndroidUtilities.usingHardwareInput || AndroidUtilities.isInMultiwindow || parentFragment != null && parentFragment.isInBubbleMode() || isPaused ? 0 : 2, 0); + if (delegate != null) { + delegate.onKeyboardRequested(); + } if (messageEditText != null) { messageEditText.requestFocus(); } @@ -9438,6 +9452,9 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific if (hasBotWebView() && botCommandsMenuIsShowing()) { return; } + if (delegate != null) { + delegate.onKeyboardRequested(); + } if (messageEditText != null && !AndroidUtilities.showKeyboard(messageEditText)) { messageEditText.clearFocus(); messageEditText.requestFocus(); @@ -9452,6 +9469,10 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific return emojiViewVisible || botKeyboardViewVisible; } + public boolean closeCreationLinkDialog() { + return messageEditText != null && messageEditText.closeCreationLinkDialog(); + } + public boolean isKeyboardVisible() { return keyboardVisible; } @@ -9767,7 +9788,7 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific 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; @@ -10866,6 +10887,14 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific 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() { @@ -10894,6 +10923,13 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific 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 99f92bbea..b1f1410c0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java @@ -70,6 +70,7 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; 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; @@ -130,6 +131,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N public boolean canOpenPreview = false; private boolean isSoundPicker = false; public boolean isStoryLocationPicker = false; + public boolean isStoryAudioPicker = false; private ImageUpdater.AvatarFor setAvatarFor; public boolean pinnedToTop; @@ -151,11 +153,11 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N 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); @@ -390,7 +392,11 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N } 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(); + } } } @@ -1828,14 +1834,14 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N 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() { @@ -2147,7 +2153,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N } 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; } @@ -2203,7 +2209,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N } 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(); @@ -2543,7 +2549,8 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N 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); @@ -2763,7 +2770,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N 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")); @@ -2786,9 +2793,18 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N 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(); @@ -2899,6 +2915,10 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N } 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; } @@ -2975,41 +2995,48 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N }; 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) { @@ -3034,40 +3061,51 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N } 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(); + } } } @@ -3439,10 +3477,12 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N 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(); @@ -3481,6 +3521,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N } } }); + locker.lock(); NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.stopAllHeavyOperations, 512); currentSheetAnimation.start(); @@ -4004,7 +4045,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N 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); @@ -4033,6 +4074,10 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N } selectedId = 5; layoutToSet = locationLayout; + } else if (isStoryAudioPicker) { + openAudioLayout(false); + layoutToSet = audioLayout; + selectedId = 3; } else if (isSoundPicker) { openDocumentsLayout(false); layoutToSet = documentLayout; @@ -4173,11 +4218,13 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N 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; @@ -4187,12 +4234,16 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N } 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; @@ -4347,7 +4398,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N 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 8244ab60e..b80b26ba7 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.graphics.PorterDuffColorFilter; 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 @@ public class ChatAttachAlertAudioLayout extends ChatAttachAlert.AttachAlertLayou 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 2cd09ed1c..36602417f 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 class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert } return super.dispatchTouchEvent(ev); } + + @Override + public void onWebViewCreated() { + swipeContainer.setWebView(webViewContainer.getWebView()); + } }; swipeContainer = new WebViewSwipeContainer(context) { @Override @@ -481,7 +488,6 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert 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 class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert } } + 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 2b7d4330a..c72ada79b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertDocumentLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertDocumentLayout.java @@ -786,7 +786,7 @@ public class ChatAttachAlertDocumentLayout extends ChatAttachAlert.AttachAlertLa 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 fe64c5dd8..e6e8d693b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java @@ -449,7 +449,7 @@ public class ChatAttachAlertPhotoLayout extends ChatAttachAlert.AttachAlertLayou MediaController.SearchImage photoEntry1 = (MediaController.SearchImage) o; firstPhotoCaption = photoEntry1.caption; } - parentAlert.commentTextView.setText(firstPhotoCaption); + parentAlert.commentTextView.setText(AnimatedEmojiSpan.cloneSpans(firstPhotoCaption, AnimatedEmojiDrawable.CACHE_TYPE_ALERT_PREVIEW)); } } @@ -1752,16 +1752,6 @@ public class ChatAttachAlertPhotoLayout extends ChatAttachAlert.AttachAlertLayou } 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 64ecea2c2..88ee6c00a 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.AnimatorListenerAdapter; 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,13 +52,15 @@ import org.telegram.ui.ActionBar.SimpleTextView; 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; 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; @@ -97,6 +100,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; @@ -458,7 +465,9 @@ public class ChatAvatarContainer extends FrameLayout implements NotificationCent 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 1dcbe3cee..5786c6e6a 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 @@ public class ChatThemeBottomSheet extends BottomSheet implements NotificationCen @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 d96429fda..8860a524e 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 class ClippingImageView extends View { 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 4359b7e43..f7a6cf29c 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 class CombinedDrawable extends Drawable implements Drawable.Callback { } } + 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 64d825ba7..ef7fd9a17 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 class DrawingInBackgroundThreadDrawable implements NotificationCenter.Not 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 b110fc991..c5e875976 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.BuildVars; 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; @@ -78,6 +79,7 @@ public class EditTextBoldCursor extends EditTextEffects { private GradientDrawable gradientDrawable; private SubstringLayoutAnimator hintAnimator; + float rightHintOffset; private Runnable invalidateRunnable = new Runnable() { @Override @@ -100,7 +102,11 @@ public class EditTextBoldCursor extends EditTextEffects { 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; @@ -151,6 +157,20 @@ public class EditTextBoldCursor extends EditTextEffects { private List registeredTextWatchers = new ArrayList<>(); private boolean isTextWatchersSuppressed = false; + 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; @@ -202,8 +222,19 @@ public class EditTextBoldCursor extends EditTextEffects { 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 @@ -478,6 +509,9 @@ public class EditTextBoldCursor extends EditTextEffects { if (hintAnimatedDrawable != null) { hintAnimatedDrawable.setTextColor(hintColor); } + if (hintAnimatedDrawable2 != null) { + hintAnimatedDrawable2.setTextColor(hintColor); + } invalidate(); } @@ -491,6 +525,9 @@ public class EditTextBoldCursor extends EditTextEffects { if (hintAnimatedDrawable != null) { hintAnimatedDrawable.setTextSize(AndroidUtilities.dp(size)); } + if (hintAnimatedDrawable2 != null) { + hintAnimatedDrawable2.setTextSize(AndroidUtilities.dp(size)); + } super.setTextSize(unit, size); } @@ -548,11 +585,18 @@ public class EditTextBoldCursor extends EditTextEffects { 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); } @@ -560,10 +604,14 @@ public class EditTextBoldCursor extends EditTextEffects { } 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 { @@ -577,7 +625,7 @@ public class EditTextBoldCursor extends EditTextEffects { 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(); @@ -585,12 +633,12 @@ public class EditTextBoldCursor extends EditTextEffects { } 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); } } @@ -648,9 +696,9 @@ public class EditTextBoldCursor extends EditTextEffects { @Override public int getExtendedPaddingTop() { - if (ignoreClipTop) { - return 0; - } +// if (ignoreClipTop) { +// return 0; +// } if (ignoreTopCount != 0) { ignoreTopCount--; return 0; @@ -694,75 +742,103 @@ public class EditTextBoldCursor extends EditTextEffects { } 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; - } - 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; - } - } - invalidate(); + 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; } - 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; + hintLastUpdateTime = newTime; + if (hintVisible) { + hintAlpha += dt / 150.0f; + if (hintAlpha > 1.0f) { + hintAlpha = 1.0f; } - if (supportRtlHint && LocaleController.isRTL) { - float offset = getMeasuredWidth() - hintWidth; - canvas.translate(left + getScrollX() + offset, lineY - hintLayout.getHeight() - AndroidUtilities.dp(7)); - } else { - canvas.translate(left + getScrollX(), lineY - hintLayout.getHeight() - AndroidUtilities.dp2(7)); + } else { + hintAlpha -= dt / 150.0f; + if (hintAlpha < 0.0f) { + hintAlpha = 0.0f; } - 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); - } - 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) { + } + invalidate(); + } + if (hintAnimatedDrawable != null && !TextUtils.isEmpty(hintAnimatedDrawable.getText()) && (hintVisible || hintAlpha != 0)) { + if (hintAnimatedDrawable2 != null) { + if (hintAnimatedDrawable.getCurrentWidth() + hintAnimatedDrawable2.getCurrentWidth() < getMeasuredWidth()) { canvas.save(); - canvas.clipRect(0, 0, getMeasuredWidth(), getMeasuredHeight()); - hintAnimator.draw(canvas, getPaint()); + 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.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); + } + } 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(); + + 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); + } + 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 da750bafa..4ef732c56 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextCaption.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextCaption.java @@ -48,6 +48,7 @@ import org.telegram.messenger.MediaDataController; import org.telegram.messenger.R; 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; @@ -72,6 +73,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(); @@ -165,7 +168,12 @@ public class EditTextCaption extends EditTextBoldCursor { } 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()) { @@ -229,24 +237,57 @@ public class EditTextCaption extends EditTextBoldCursor { } }); 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()); + } + } 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()); } - editText.setSelection(0, editText.getText().length()); } } + public boolean closeCreationLinkDialog() { + if (creationLinkDialog != null && creationLinkDialog.isShowing()) { + creationLinkDialog.dismiss(); + return true; + } + return false; + } + public void makeSelectedRegular() { applyTextStyleToSelection(null); } @@ -541,7 +582,7 @@ public class EditTextCaption extends EditTextBoldCursor { } } 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 a76f3328f..1e86ec245 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.content.ContextWrapper; 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 @@ public class EditTextEmoji extends FrameLayout implements NotificationCenter.Not @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 @@ public class EditTextEmoji extends FrameLayout implements NotificationCenter.Not 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 @@ public class EditTextEmoji extends FrameLayout implements NotificationCenter.Not 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 @@ public class EditTextEmoji extends FrameLayout implements NotificationCenter.Not 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 @@ public class EditTextEmoji extends FrameLayout implements NotificationCenter.Not 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 class EditTextEmoji extends FrameLayout implements NotificationCenter.Not } } + 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 class EditTextEmoji extends FrameLayout implements NotificationCenter.Not 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 class EditTextEmoji extends FrameLayout implements NotificationCenter.Not 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 class EditTextEmoji extends FrameLayout implements NotificationCenter.Not public void onAnimationEnd(Animator animation) { isAnimatePopupClosing = false; emojiView.setTranslationY(0); + emojiView.setAlpha(0); bottomPanelTranslationY(0); hideEmojiView(); } @@ -474,7 +505,6 @@ public class EditTextEmoji extends FrameLayout implements NotificationCenter.Not emojiView.setVisibility(VISIBLE); emojiViewVisible = true; - onEmojiKeyboardUpdate(); View currentView = emojiView; if (keyboardHeight <= 0) { @@ -505,25 +535,31 @@ public class EditTextEmoji extends FrameLayout implements NotificationCenter.Not 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 class EditTextEmoji extends FrameLayout implements NotificationCenter.Not } 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 class EditTextEmoji extends FrameLayout implements NotificationCenter.Not @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).commit(); 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 7054d692e..f6350368a 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 class FilterGLThread extends DispatchQueue { 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 class FilterGLThread extends DispatchQueue { 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 @@ public class FilterGLThread extends DispatchQueue { } 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 @@ public class FilterGLThread extends DispatchQueue { finish(); return false; } + if (blurManager != null) { + blurManager.acquiredContext(eglContext); + } if (surfaceTexture instanceof SurfaceTexture) { eglSurface = egl10.eglCreateWindowSurface(eglDisplay, eglConfig, surfaceTexture, null); @@ -237,6 +266,31 @@ public class FilterGLThread extends DispatchQueue { 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 @@ public class FilterGLThread extends DispatchQueue { 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 class FilterGLThread extends DispatchQueue { 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 class FilterGLThread extends DispatchQueue { eglSurface = null; } if (eglContext != null) { + if (blurManager != null) { + blurManager.destroyedContext(eglContext); + } egl10.eglDestroyContext(eglDisplay, eglContext); eglContext = null; } @@ -356,55 +425,82 @@ public class FilterGLThread extends DispatchQueue { private boolean filterTextureAvailable; - private Runnable drawRunnable = new Runnable() { - @Override - public void run() { - if (!initied) { - return; + private final Runnable drawRunnable = () -> { + if (!initied) { + return; + } + + makeCurrentContext(); + + if (updateSurface) { + videoSurfaceTexture.updateTexImage(); + videoSurfaceTexture.getTransformMatrix(videoTextureMatrix); + setRenderData(); + updateSurface = false; + filterShaders.onVideoFrameUpdate(videoTextureMatrix); + videoFrameAvailable = true; + } + + 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); } - makeCurrentContext(); + return; + } - if (updateSurface) { - videoSurfaceTexture.updateTexImage(); - videoSurfaceTexture.getTransformMatrix(videoTextureMatrix); - setRenderData(); - updateSurface = false; - filterShaders.onVideoFrameUpdate(videoTextureMatrix); - videoFrameAvailable = true; + 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 (!renderDataSet) { - return; - } + if (filterTextureAvailable) { + GLES20.glViewport(0, 0, surfaceWidth, surfaceHeight); + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); - 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; - } + int tex = filterShaders.getRenderTexture(blurred ? 0 : 1); - if (filterTextureAvailable) { - GLES20.glViewport(0, 0, surfaceWidth, surfaceHeight); - GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + GLES20.glUseProgram(simpleShaderProgram); + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex); - 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.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 @@ public class FilterGLThread extends DispatchQueue { 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 0ec78a981..637523188 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 class FilterShaders { } } - private static final String simpleVertexVideoShaderCode = + public static final String simpleVertexVideoShaderCode = "attribute vec4 position;" + "uniform mat4 videoMatrix;" + "attribute vec4 inputTexCoord;" + @@ -1114,7 +1114,8 @@ public class FilterShaders { -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 class FilterShaders { -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 class FilterShaders { 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 @@ public class FilterShaders { 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 33d8a6cd7..7b02f1866 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.messenger.SharedConfig; 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 @@ public class FloatingDebugView extends FrameLayout implements NotificationCenter 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 4012488ad..32d0cf3dd 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.Path; 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 @@ import org.telegram.ui.Cells.ChatMessageCell; @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 class HintView extends FrameLayout { } 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 class HintView extends FrameLayout { } 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 class HintView extends FrameLayout { 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 class HintView extends FrameLayout { 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 class HintView extends FrameLayout { 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 29133a390..ee98672b9 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.Bitmap; 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.NonNull; import androidx.annotation.Nullable; import androidx.core.graphics.ColorUtils; -import com.google.android.exoplayer2.util.Consumer; - import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.BotWebViewVibrationEffect; import org.telegram.messenger.LocaleController; @@ -225,6 +224,22 @@ public class ItemOptions { 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 class ItemOptions { ((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 class ItemOptions { 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 class ItemOptions { } } + private float offsetX, offsetY; public ItemOptions show() { if (actionBarPopupWindow != null) { return this; @@ -323,7 +365,7 @@ public class ItemOptions { 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 class ItemOptions { 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 class ItemOptions { } 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 class ItemOptions { 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 class ItemOptions { 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 b4c174275..ecb09bb49 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.TLObject; 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 class JoinGroupAlert extends BottomSheet { 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 class JoinGroupAlert extends BottomSheet { 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 class JoinGroupAlert extends BottomSheet { 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 class JoinGroupAlert extends BottomSheet { } } + 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 @@ public class JoinGroupAlert extends BottomSheet { } 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 3e9f73c14..efe52e57a 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.text.TextPaint; 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 6108e1791..9a987266d 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 class MediaActivity extends BaseFragment implements SharedMediaLayout.Sha 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 @@ public class MediaActivity extends BaseFragment implements SharedMediaLayout.Sha 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 @@ public class MediaActivity extends BaseFragment implements SharedMediaLayout.Sha @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 @@ public class MediaActivity extends BaseFragment implements SharedMediaLayout.Sha 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 class MediaActivity extends BaseFragment implements SharedMediaLayout.Sha 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 @@ public class MediaActivity extends BaseFragment implements SharedMediaLayout.Sha 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 @@ public class MediaActivity extends BaseFragment implements SharedMediaLayout.Sha } 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 class MediaActivity extends BaseFragment implements SharedMediaLayout.Sha 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 class MediaActivity extends BaseFragment implements SharedMediaLayout.Sha ); } - 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); - } - - 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; + super(context, resourcesProvider); } @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); + 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 + 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 9955f695c..e4c1c3629 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 @@ public class Input { } 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 d3197180f..2a43868fe 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 androidx.core.graphics.ColorUtils; 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 class Painting { 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 class Painting { 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 @@ public class Painting { } 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 class Painting { } 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 @@ public class Painting { 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 class Painting { } 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()))); - - 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.glActiveTexture(GLES20.GL_TEXTURE1); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getPaintTexture()); - - 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()); + 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.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ZERO); + 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.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.glViewport(0, 0, (int) size.width, (int) size.height); - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + Shader shader = shaders.get(brush.getShaderName(Brush.PAINT_TYPE_COMPOSITE)); + if (shader == null) { + return null; + } - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTexture()); - GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + GLES20.glUseProgram(shader.program); + + 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.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.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 @@ public class Painting { } endSuppressingChanges(); - renderState.reset(); activePath = null; @@ -600,7 +649,7 @@ public class Painting { helperApplyAlpha = 0f; } - private Slice registerUndo(RectF rect) { + private Slice registerUndo(RectF rect, boolean blurTex) { if (rect == null) { return null; } @@ -610,12 +659,33 @@ public class Painting { 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 @@ public class Painting { 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 class Painting { 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 class Painting { } } + 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 @@ public class Painting { 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 @@ public class Painting { 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 @@ public class Painting { } 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 class Painting { 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 class Painting { 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 class Painting { 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 class Painting { 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 class Painting { 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 class Painting { 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 5ac26e524..318079c88 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.AndroidUtilities; 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 @@ public class RenderView extends TextureView { private Input input; private ShapeInput shapeInput; private Bitmap bitmap; + private Bitmap blurBitmap; private boolean transformedBitmap; private boolean firstDrawSent; @@ -57,11 +59,12 @@ public class RenderView extends TextureView { private boolean shuttingDown; - 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); @@ -71,7 +74,7 @@ public class RenderView extends TextureView { if (surface == null || internal != null) { return; } - internal = new CanvasInternal(surface); + internal = new CanvasInternal(surface, blurManager); internal.setBufferSize(width, height); updateTransform(); @@ -342,8 +345,11 @@ public class RenderView extends TextureView { 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; } @@ -408,7 +414,8 @@ public class RenderView extends TextureView { } 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())); @@ -416,6 +423,10 @@ public class RenderView extends TextureView { finish(); return false; } + if (blurManager != null) { + blurManager.acquiredContext(eglContext); + blurManager.attach(safeRequestRender); + } if (surfaceTexture instanceof SurfaceTexture) { eglSurface = egl10.eglCreateWindowSurface(eglDisplay, eglConfig, surfaceTexture, null); @@ -446,7 +457,7 @@ public class RenderView extends TextureView { painting.setupShaders(); checkBitmap(); - painting.setBitmap(bitmap); + painting.setBitmap(bitmap, blurBitmap); Utils.HasGLError(); @@ -468,6 +479,13 @@ public class RenderView extends TextureView { 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() { @@ -520,9 +538,18 @@ public class RenderView extends TextureView { } 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); @@ -544,6 +571,9 @@ public class RenderView extends TextureView { eglSurface = null; } if (eglContext != null) { + if (blurManager != null) { + blurManager.destroyedContext(eglContext); + } egl10.eglDestroyContext(eglDisplay, eglContext); eglContext = null; } @@ -551,6 +581,9 @@ public class RenderView extends TextureView { egl10.eglTerminate(eglDisplay); eglDisplay = null; } + if (blurManager != null) { + blurManager.detach(safeRequestRender); + } } public void shutdown() { @@ -564,6 +597,10 @@ public class RenderView extends TextureView { } public Bitmap getTexture() { + return getTexture(false, false); + } + + public Bitmap getTexture(final boolean onlyBlur, final boolean includeBlur) { if (!initialized) { return null; } @@ -571,7 +608,7 @@ public class RenderView extends TextureView { 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; } @@ -585,11 +622,11 @@ public class RenderView extends TextureView { } } - 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 24fe0a086..1a1900436 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 @@ public class ShaderSet { 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 c2d05b1d7..e1408685a 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.Deflater; 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 class Slice { 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 39caaf477..8a0415231 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 @@ package org.telegram.ui.Components.Paint; 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 c42127e7c..cef38cfc4 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 @@ public class EditTextOutline extends EditTextBoldCursor { } 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 @@ public class EditTextOutline extends EditTextBoldCursor { 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 @@ public class EditTextOutline extends EditTextBoldCursor { 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 e5f6d1734..aa0119bff 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.Canvas; 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; @@ -64,6 +62,7 @@ public class EntityView extends FrameLayout { default void onEntityDragMultitouchEnd() {} default void onEntityDragEnd(boolean delete) {} default void onEntityDragTrash(boolean enter) {} + default void onEntityHandleTouched() {} } private float previousLocationX, previousLocationY; @@ -139,6 +138,10 @@ public class EntityView extends FrameLayout { return 100f; } + protected float getMinScale() { + return 0f; + } + public float getScale() { return getScaleX(); } @@ -228,13 +231,13 @@ public class EntityView extends FrameLayout { 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) { @@ -379,7 +382,7 @@ public class EntityView extends FrameLayout { // 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) { @@ -583,17 +586,22 @@ public class EntityView extends FrameLayout { 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) { @@ -879,6 +887,9 @@ public class EntityView extends FrameLayout { if (getParent() instanceof EntitiesContainerView) { ((EntitiesContainerView) getParent()).invalidate(); } + if (handle == SELECTION_WHOLE_HANDLE && allowLongPressOnSelected()) { + AndroidUtilities.runOnUIThread(longPressRunnable, ViewConfiguration.getLongPressTimeout()); + } } } break; @@ -892,6 +903,9 @@ public class EntityView extends FrameLayout { 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); @@ -921,10 +935,9 @@ public class EntityView extends FrameLayout { } 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); @@ -950,6 +963,10 @@ public class EntityView extends FrameLayout { } } + 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 e81339f09..4c7a3d440 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.Layout; 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; @@ -88,6 +89,7 @@ import org.telegram.ui.Components.Paint.UndoStore; 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; @@ -97,9 +99,9 @@ import java.util.ArrayList; 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; @@ -126,10 +128,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; @@ -138,7 +140,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() { @@ -166,7 +168,7 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh private TextView textTab; private PaintToolsView paintToolsView; - private PaintTextOptionsView textOptionsView; + public PaintTextOptionsView textOptionsView; private PaintTypefaceListView typefaceListView; private ImageView undoButton; private LinearLayout zoomOutButton; @@ -279,7 +281,7 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh 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); @@ -913,6 +915,25 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh 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(); @@ -1379,7 +1400,8 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh 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); @@ -1403,6 +1425,10 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh } } + 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); @@ -1594,7 +1620,7 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh @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; @@ -1891,6 +1917,7 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh tx = trX; ty = trY; } + ty += - emojiPadding / 2f; float finalScale = scale * additionlScale; if (Float.isNaN(finalScale)) { finalScale = 1f; @@ -2059,9 +2086,9 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh 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)); @@ -2835,8 +2862,8 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh /* === 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; @@ -2867,6 +2894,9 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh 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(); @@ -2874,6 +2904,10 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh } } + protected void onEmojiViewCloseByClick() { + + } + private void showEmojiPopup(int show) { final boolean ignore = bottomPanelIgnoreOnce; bottomPanelIgnoreOnce = false; @@ -2912,6 +2946,7 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh emojiPadding = emojiWasPadding = currentHeight; requestLayout(); + updateKeyboard(); ChatActivityEnterViewAnimatedIconView emojiButton = textOptionsView.getEmojiButton(); if (emojiButton != null) { @@ -2921,32 +2956,32 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh 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(); } @@ -2966,15 +3001,14 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh 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) { @@ -2987,13 +3021,13 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh 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() { @@ -3001,10 +3035,10 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh 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(); } }); @@ -3019,12 +3053,12 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh 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 @@ -3035,12 +3069,15 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh 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); } @@ -3078,6 +3115,7 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh emojiPadding = emojiWasPadding = layoutParams.height; requestLayout(); + updateKeyboard(); onWindowSizeChanged(); } } @@ -3102,6 +3140,7 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh if (emojiPadding != 0 && !keyboardVisible && keyboardVisible != oldValue && !emojiViewVisible) { emojiPadding = 0; requestLayout(); + updateKeyboard(); } if (oldValue && !keyboardVisible && emojiPadding > 0 && translateBottomPanelAfterResize) { translateBottomPanelAfterResize = false; @@ -3113,11 +3152,9 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh AndroidUtilities.cancelRunOnUIThread(openKeyboardRunnable); } onWindowSizeChanged(); - - updatePlusEmojiKeyboardButton(); } - private void updatePlusEmojiKeyboardButton() { + public void updatePlusEmojiKeyboardButton() { if (textOptionsView != null) { if (keyboardVisible) { textOptionsView.animatePlusToIcon(R.drawable.input_smile); @@ -3284,55 +3321,19 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh 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); } @@ -3340,7 +3341,6 @@ public class LPhotoPaintView extends SizeNotifierFrameLayoutPhoto implements IPh 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 8a170d712..96b90672f 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.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.PorterDuff; import android.graphics.PorterDuffColorFilter; @@ -23,7 +24,6 @@ import android.os.Build; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; -import android.util.Log; import android.util.TypedValue; import android.view.Gravity; import android.view.HapticFeedbackConstants; @@ -37,7 +37,7 @@ import android.widget.ImageView; 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.ActionBar.Theme; import org.telegram.ui.BubbleActivity; import org.telegram.ui.Cells.PhotoEditRadioCell; import org.telegram.ui.Cells.PhotoEditToolCell; -import org.telegram.ui.Stories.recorder.PreviewView; import org.telegram.ui.Stories.recorder.StoryRecorder; import java.nio.ByteBuffer; @@ -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 class PhotoFilterView extends FrameLayout implements FilterShaders.Filter } } - 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 class PhotoFilterView extends FrameLayout implements FilterShaders.Filter blurExcludeSize = state.blurExcludeSize; blurExcludePoint = state.blurExcludePoint; blurExcludeBlurSize = state.blurExcludeBlurSize; + filtersEmpty = state.isEmpty(); blurAngle = state.blurAngle; lastState = state; } else { @@ -380,6 +381,7 @@ public class PhotoFilterView extends FrameLayout implements FilterShaders.Filter 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 class PhotoFilterView extends FrameLayout implements FilterShaders.Filter }); } 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 class PhotoFilterView extends FrameLayout implements FilterShaders.Filter @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 class PhotoFilterView extends FrameLayout implements FilterShaders.Filter curvesControl = new PhotoFilterCurvesControl(context, curvesToolValue); curvesControl.setDelegate(() -> { + updateFiltersEmpty(); if (eglThread != null) { eglThread.requestRender(false); } @@ -834,6 +849,26 @@ public class PhotoFilterView extends FrameLayout implements FilterShaders.Filter } } + 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 class PhotoFilterView extends FrameLayout implements FilterShaders.Filter @Override public boolean shouldShowOriginal() { - return showOriginal; + return showOriginal || filtersEmpty; } @Override @@ -1138,6 +1173,7 @@ public class PhotoFilterView extends FrameLayout implements FilterShaders.Filter 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 class PhotoFilterView extends FrameLayout implements FilterShaders.Filter if (eglThread != null) { eglThread.requestRender(true); } + updateFiltersEmpty(); }); } else { view = new PhotoEditRadioCell(mContext); @@ -1217,6 +1254,7 @@ public class PhotoFilterView extends FrameLayout implements FilterShaders.Filter if (eglThread != null) { eglThread.requestRender(false); } + updateFiltersEmpty(); }); } return new RecyclerListView.Holder(view); @@ -1449,4 +1487,21 @@ public class PhotoFilterView extends FrameLayout implements FilterShaders.Filter } } } + + 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 1615d3bb7..000000000 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoPaintView.java +++ /dev/null @@ -1,1753 +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.*; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.os.Looper; -import android.text.TextUtils; -import android.util.SparseArray; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.google.android.gms.vision.Frame; -import com.google.android.gms.vision.face.Face; -import com.google.android.gms.vision.face.FaceDetector; - -import org.telegram.messenger.AndroidUtilities; -import org.telegram.messenger.Bitmaps; -import org.telegram.messenger.BuildVars; -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.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.PhotoFace; -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.Components.Paint.UndoStore; -import org.telegram.ui.Components.Paint.Brush; -import org.telegram.ui.Components.Paint.RenderView; -import org.telegram.ui.Components.Paint.Painting; -import org.telegram.ui.Components.Paint.Swatch; -import org.telegram.ui.Components.Paint.Views.ColorPicker; -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); - 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(); - } - }); - - 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); - size.width = 1280; - size.height = (float) Math.floor(size.width * height / width); - if (size.height > 1280) { - size.height = 1280; - 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.MULTIPLY)); - 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.MULTIPLY)); - } - 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.MULTIPLY)); - 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.MULTIPLY)); - 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 c9272da4e..6bdd698eb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java @@ -63,7 +63,7 @@ import org.telegram.ui.PhotoViewer; import org.telegram.ui.PremiumPreviewFragment; import org.telegram.ui.Stories.DarkThemeResourceProvider; -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 501ca2370..d127a3262 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 class PipVideoOverlay { } 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 @@ public class PipVideoOverlay { // 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 9fe2ff11a..de529582e 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 PollVotesAlert extends BottomSheet { private SimpleTextView nameTextView; private AvatarDrawable avatarDrawable; + private StatusBadgeComponent statusBadgeComponent; private TLRPC.User currentUser; private TLRPC.Chat currentChat; @@ -297,13 +298,14 @@ public class PollVotesAlert extends BottomSheet { 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 @@ public class PollVotesAlert extends BottomSheet { 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 class PollVotesAlert extends BottomSheet { 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 3d78fc3b1..21620c734 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 class LimitPreviewView extends LinearLayout { @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 class LimitPreviewView extends LinearLayout { 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 class LimitPreviewView extends LinearLayout { 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 class LimitPreviewView extends LinearLayout { @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 @@ public class LimitPreviewView extends LinearLayout { } 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 bd8a780c9..95adcbaca 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.annotation.SuppressLint; 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.LinearLayout; 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.HeaderCell; 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 class LimitReachedBottomSheet extends BottomSheetWithRecyclerListView { 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 @@ public class LimitReachedBottomSheet extends BottomSheetWithRecyclerListView { 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 @@ public class LimitReachedBottomSheet extends BottomSheetWithRecyclerListView { } 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 class LimitReachedBottomSheet extends BottomSheetWithRecyclerListView { 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 class LimitReachedBottomSheet extends BottomSheetWithRecyclerListView { 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 class LimitReachedBottomSheet extends BottomSheetWithRecyclerListView { }; } + 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 class LimitReachedBottomSheet extends BottomSheetWithRecyclerListView { updateButton(); } + public void setDialogId(long dialogId) { + this.dialogId = dialogId; + } + private class HeaderView extends LinearLayout { @SuppressLint("SetTextI18n") @@ -810,6 +845,7 @@ public class LimitReachedBottomSheet extends BottomSheetWithRecyclerListView { 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 5989fbf21..7d94d33d6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieDrawable.java @@ -309,6 +309,8 @@ public class RLottieDrawable extends BitmapDrawable implements Animatable, Bitma } private boolean genCacheSend; + private boolean allowDrawFramesWhileCacheGenerating; + protected Runnable loadFrameRunnable = new Runnable() { private long lastUpdate = 0; @@ -363,6 +365,10 @@ public class RLottieDrawable extends BitmapDrawable implements Animatable, Bitma 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); } @@ -374,7 +380,14 @@ public class RLottieDrawable extends BitmapDrawable implements Animatable, Bitma 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); @@ -500,7 +513,7 @@ public class RLottieDrawable extends BitmapDrawable implements Animatable, Bitma 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) { @@ -986,7 +999,7 @@ public class RLottieDrawable extends BitmapDrawable implements Animatable, Bitma if (loadFrameTask != null || nextRenderingBitmap != null || !canLoadFrames() || loadingInBackground || destroyWhenDone || !isRunning && (!decodeSingleFrame || decodeSingleFrame && singleFrameDecoded)) { return false; } - if (generatingCache) { + if (generatingCache && !allowDrawFramesWhileCacheGenerating) { return false; } if (!newColorUpdates.isEmpty()) { @@ -1335,7 +1348,6 @@ public class RLottieDrawable extends BitmapDrawable implements Animatable, Bitma return -1; } int framesPerUpdates = shouldLimitFps ? 2 : 1; - int result = getFrame(generateCacheNativePtr, generateCacheFramePointer, bitmap, width, height, bitmap.getRowBytes(), true); if (result == -5) { try { @@ -1395,10 +1407,6 @@ public class RLottieDrawable extends BitmapDrawable implements Animatable, Bitma } } - public void setAllowDrawFramesWhileCacheGenerating(boolean allowDrawWhileCacheGenerating) { - //TODO - } - private class NativePtrArgs { public int[] colorReplacement; public int fitzModifier; @@ -1438,6 +1446,10 @@ public class RLottieDrawable extends BitmapDrawable implements Animatable, Bitma } } + 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 8437bcbba..aa9b83375 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.LiteMode; 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 @@ public class AnimatedEmojiEffect { 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 class AnimatedEmojiEffect { } } 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 class AnimatedEmojiEffect { firsDraw = false; } - public boolean done() { + public boolean isDone() { return System.currentTimeMillis() - startTime > 2500; } @@ -118,7 +132,13 @@ public class AnimatedEmojiEffect { 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 class AnimatedEmojiEffect { 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 6024ed7d5..bfda46e45 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 @@ public class ChatSelectionReactionMenuOverlay extends FrameLayout { } 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 f17ccc7f5..377fcbefe 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 @@ public class CustomEmojiReactionsWindow { 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 @@ public class CustomEmojiReactionsWindow { 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 @@ public class CustomEmojiReactionsWindow { 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 @@ public class CustomEmojiReactionsWindow { } } + 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 000000000..a40e8b720 --- /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 8d7c4928e..6d5713f4e 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 @@ public class ReactionsEffectOverlay { 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 b3798b233..ef0f86d56 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 class ReactionsLayoutInBubble { 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 832579cc2..7c69975f7 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 class ReactionsUtils { 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 406dfebd1..f813b5566 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.RectF; 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.tgnet.TLRPC; 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; @@ -97,7 +96,6 @@ public class ReactionsContainerLayout extends FrameLayout implements Notificatio 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; @@ -116,6 +114,7 @@ public class ReactionsContainerLayout extends FrameLayout implements Notificatio private MessageObject messageObject; private int currentAccount; private long waitingLoadingChatId; + private boolean isTop; private boolean mirrorX; private boolean isFlippedVertically; @@ -132,6 +131,7 @@ public class ReactionsContainerLayout extends FrameLayout implements Notificatio private LinearLayoutManager linearLayoutManager; private RecyclerView.Adapter listAdapter; + RectF rectF = new RectF(); HashSet selectedReactions = new HashSet<>(); @@ -176,6 +176,9 @@ public class ReactionsContainerLayout extends FrameLayout implements Notificatio 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); @@ -354,7 +357,7 @@ public class ReactionsContainerLayout extends FrameLayout implements Notificatio 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); } @@ -624,7 +627,7 @@ public class ReactionsContainerLayout extends FrameLayout implements Notificatio allReactionsList.clear(); allReactionsList.addAll(visibleReactionsList); // checkPremiumReactions(this.visibleReactionsList); - int size = getLayoutParams().height - getPaddingTop() - getPaddingBottom(); + int size = getLayoutParams().height - (hasHint ? AndroidUtilities.dp(20) : 0) - getPaddingTop() - getPaddingBottom(); if (size * visibleReactionsList.size() < AndroidUtilities.dp(200)) { getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT; } @@ -646,6 +649,10 @@ public class ReactionsContainerLayout extends FrameLayout implements Notificatio invalidate(); } + if (hintView != null) { + hintView.setTranslationY(-expandSize()); + } + float cPr = (Math.max(CLIP_PROGRESS, Math.min(transitionProgress, 1f)) - CLIP_PROGRESS) / (1f - CLIP_PROGRESS); float br = bigCircleRadius * cPr, sr = smallCircleRadius * cPr; @@ -691,8 +698,9 @@ public class ReactionsContainerLayout extends FrameLayout implements Notificatio chatScrimPopupContainerLayout.setExpandSize(expandSize); } float transitionLeftOffset = (getWidth() - getPaddingRight()) * Math.min(1f, lt); + float hintHeight = (hasHint ? AndroidUtilities.dp(20) : 0); rect.set(getPaddingLeft() + transitionLeftOffset, getPaddingTop() + recyclerListView.getMeasuredHeight() * (1f - otherViewsScale) - expandSize, (getWidth() - getPaddingRight()) * rt, getHeight() - getPaddingBottom() + expandSize); - radius = (rect.height() - expandSize * 2f) / 2f; + radius = ((rect.height() - hintHeight) - expandSize * 2f) / 2f; if (type != TYPE_STORY) { shadow.setAlpha((int) (Utilities.clamp(1f - (customEmojiReactionsEnterProgress / 0.05f), 1f, 0f) * 255)); @@ -865,12 +873,15 @@ public class ReactionsContainerLayout extends FrameLayout implements Notificatio if (type == TYPE_STORY) { return; } - float bigCy; canvas.save(); - canvas.clipRect(0, AndroidUtilities.lerp(rect.bottom, 0, CubicBezierInterpolator.DEFAULT.getInterpolation(flipVerticalProgress)) - (int) Math.ceil(rect.height() / 2f * (1f - transitionProgress)), getMeasuredWidth(), AndroidUtilities.lerp(getMeasuredHeight() + AndroidUtilities.dp(8), getPaddingTop() - expandSize(), CubicBezierInterpolator.DEFAULT.getInterpolation(flipVerticalProgress))); + if (isTop) { + canvas.clipRect(0, 0, getMeasuredWidth(), AndroidUtilities.lerp(rect.top, getMeasuredHeight(), CubicBezierInterpolator.DEFAULT.getInterpolation(flipVerticalProgress)) - (int) Math.ceil(rect.height() / 2f * (1f - transitionProgress)) + 1); + } else { + canvas.clipRect(0, AndroidUtilities.lerp(rect.bottom, 0, CubicBezierInterpolator.DEFAULT.getInterpolation(flipVerticalProgress)) - (int) Math.ceil(rect.height() / 2f * (1f - transitionProgress)) - 1, getMeasuredWidth(), AndroidUtilities.lerp(getMeasuredHeight() + AndroidUtilities.dp(8), getPaddingTop() - expandSize(), CubicBezierInterpolator.DEFAULT.getInterpolation(flipVerticalProgress))); + } float cx = LocaleController.isRTL || mirrorX ? bigCircleOffset : getWidth() - bigCircleOffset; - float cy = getHeight() - getPaddingBottom() + expandSize(); - bigCy = cy = AndroidUtilities.lerp(cy, getPaddingTop() - expandSize(), CubicBezierInterpolator.DEFAULT.getInterpolation(flipVerticalProgress)); + cx += bubblesOffset; + float cy = isTop ? getPaddingTop() - expandSize() : getHeight() - getPaddingBottom() + expandSize(); int sPad = AndroidUtilities.dp(3); shadow.setAlpha(alpha); bgPaint.setAlpha(alpha); @@ -879,7 +890,8 @@ public class ReactionsContainerLayout extends FrameLayout implements Notificatio canvas.drawCircle(cx, cy, br, bgPaint); cx = LocaleController.isRTL || mirrorX ? bigCircleOffset - bigCircleRadius : getWidth() - bigCircleOffset + bigCircleRadius; - cy = getHeight() - smallCircleRadius - sPad + expandSize(); + cx += bubblesOffset; + cy = isTop ? getPaddingTop() - expandSize() - AndroidUtilities.dp(16) : getHeight() - smallCircleRadius - sPad + expandSize(); cy = AndroidUtilities.lerp(cy, smallCircleRadius + sPad - expandSize(), CubicBezierInterpolator.DEFAULT.getInterpolation(flipVerticalProgress)); sPad = -AndroidUtilities.dp(1); shadow.setBounds((int) (cx - br - sPad * cPr), (int) (cy - br - sPad * cPr), (int) (cx + br + sPad * cPr), (int) (cy + br + sPad * cPr)); @@ -1073,6 +1085,15 @@ public class ReactionsContainerLayout extends FrameLayout implements Notificatio listAdapter.notifyDataSetChanged(); } + + public void setSelectedReaction(ReactionsLayoutInBubble.VisibleReaction visibleReaction) { + selectedReactions.clear(); + if (visibleReaction != null) { + selectedReactions.add(visibleReaction); + } + listAdapter.notifyDataSetChanged(); + } + private void filterReactions(List visibleReactions) { HashSet set = new HashSet<>(); for (int i = 0; i < visibleReactions.size(); i++) { @@ -1318,6 +1339,32 @@ public class ReactionsContainerLayout extends FrameLayout implements Notificatio 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 81bddc7ef..9029c2b4e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java @@ -439,6 +439,9 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter } 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; @@ -1430,7 +1433,9 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter 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) { @@ -1739,7 +1744,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter 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); } @@ -1890,7 +1895,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter } 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) { @@ -2555,6 +2560,10 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter return false; } + protected boolean isArchivedOnlyStoriesView() { + return false; + } + protected boolean includeStories() { return true; } @@ -3127,7 +3136,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter 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); @@ -3940,7 +3949,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter 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); } @@ -6797,7 +6806,11 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter 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 69f6f1280..fd121d850 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.Bitmap; 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; @@ -53,7 +55,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; @@ -107,7 +109,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 618ddab43..f250b524a 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 android.widget.FrameLayout; 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 class SizeNotifierFrameLayoutPhoto extends FrameLayout { this.activity = activity; } - public void setDelegate(SizeNotifierFrameLayoutPhotoDelegate sizeNotifierFrameLayoutPhotoDelegate) { - delegate = sizeNotifierFrameLayoutPhotoDelegate; - } - public void setWithoutWindow(boolean value) { withoutWindow = value; } @@ -55,10 +46,12 @@ public class SizeNotifierFrameLayoutPhoto extends FrameLayout { notifyHeightChanged(); } + @Override public int getKeyboardHeight() { return keyboardHeight; } + @Override public int measureKeyboardHeight() { View rootView = getRootView(); getWindowVisibleDisplayFrame(rect); @@ -74,8 +67,9 @@ public class SizeNotifierFrameLayoutPhoto extends FrameLayout { } } + @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 000000000..d8d71c7b5 --- /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 000000000..e207e00a5 --- /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 000000000..ff3ee1813 --- /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 914015cea..0e868013d 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 class VideoEditTextureView extends TextureView implements TextureView.Sur } 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 class VideoEditTextureView extends TextureView implements TextureView.Sur 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 c1b3e0392..9fb2500fd 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.DefaultRenderersFactory; 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis private boolean isStreaming; private boolean autoplay; private boolean mixedAudio; + public boolean allowMultipleInstances; private boolean triedReinit; @@ -166,7 +169,7 @@ public class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 @@ public class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis } 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 2e0a39a61..6b49b0563 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.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.drawable.Drawable; @@ -31,6 +32,7 @@ import android.view.View; 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 @@ import java.util.ArrayList; 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 class VideoTimelinePlayView extends View { 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 class VideoTimelinePlayView extends View { 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 class VideoTimelinePlayView extends View { 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 @@ public class VideoTimelinePlayView extends View { 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 @@ public class VideoTimelinePlayView extends View { 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 class VideoTimelinePlayView extends View { 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 @@ public class VideoTimelinePlayView extends View { } 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 @@ public class VideoTimelinePlayView extends View { } 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 class VideoTimelinePlayView extends View { 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 2653730a5..93e361d51 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.StaticLayout; 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 class ViewPagerFixed extends FrameLayout { } 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 class ViewPagerFixed extends FrameLayout { 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 class ViewPagerFixed extends FrameLayout { 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 class ViewPagerFixed extends FrameLayout { 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 @@ public class ViewPagerFixed extends FrameLayout { viewPages[1].setTranslationX(-viewPages[0].getMeasuredWidth()); } } - onTabAnimationUpdate(); + onTabAnimationUpdate(false); return true; } @@ -471,10 +477,12 @@ public class ViewPagerFixed extends FrameLayout { 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 class ViewPagerFixed extends FrameLayout { } } } + 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 class ViewPagerFixed extends FrameLayout { tabsView.setEnabled(true); } - onTabAnimationUpdate(); + onTabAnimationUpdate(false); onScrollEnd(); } }); @@ -626,7 +635,7 @@ public class ViewPagerFixed extends FrameLayout { tabsAnimationInProgress = true; startedTracking = false; - onTabAnimationUpdate(); + onTabAnimationUpdate(false); } else { maybeStartTracking = false; if (tabsView != null) { @@ -648,6 +657,7 @@ public class ViewPagerFixed extends FrameLayout { 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 class ViewPagerFixed extends FrameLayout { 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 000000000..ac65b446f --- /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 0374ea202..a77f671d1 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 e8bd1c946..980426c61 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 class DefaultThemesPreviewCell extends LinearLayout { 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 e3bc4dc2e..330a5514b 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.Build; 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.StateSet; @@ -154,6 +153,7 @@ import org.telegram.ui.Cells.RequestPeerRequirementsCell; 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; @@ -208,7 +208,6 @@ import org.telegram.ui.Components.ViewPagerFixed; import org.telegram.ui.Stories.DialogStoriesCell; import org.telegram.ui.Stories.StoriesController; import org.telegram.ui.Stories.StoriesListPlaceProvider; -import org.telegram.ui.Stories.StoriesUtilities; import org.telegram.ui.Stories.UserListPoller; import org.telegram.ui.Stories.recorder.HintView2; import org.telegram.ui.Stories.recorder.StoryRecorder; @@ -216,6 +215,7 @@ import org.telegram.ui.Stories.recorder.StoryRecorder; 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; @@ -250,6 +250,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; @@ -440,7 +441,10 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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; @@ -647,16 +651,8 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. @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() { @@ -731,7 +727,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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()); @@ -947,6 +943,10 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } } + 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()); @@ -1057,6 +1057,9 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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(); @@ -1211,7 +1214,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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; @@ -1348,7 +1351,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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 { @@ -1534,12 +1537,28 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } } + 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) { @@ -1571,12 +1590,12 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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); } } @@ -1940,6 +1959,9 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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) { @@ -2624,6 +2646,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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); @@ -2660,7 +2683,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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); @@ -2760,6 +2783,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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); @@ -2850,6 +2874,10 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. AndroidUtilities.runOnUIThread(() -> Theme.createChatResources(context, false)); + authHintCellVisible = false; + authHintCellProgress = 0f; + authHintCell = null; + ActionBarMenu menu = actionBar.createMenu(); if (!onlySelect && searchString == null && folderId == 0) { doneItem = new ActionBarMenuItem(context, null, Theme.getColor(Theme.key_actionBarDefaultSelector), Theme.getColor(Theme.key_actionBarDefaultIcon), true); @@ -3208,7 +3236,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } 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; } @@ -4304,6 +4332,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. finishPreviewFragment(); return; } + Bundle args = new Bundle(); args.putBoolean("destroyAfterSelect", true); presentFragment(new ContactsActivity(args)); @@ -4374,7 +4403,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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; } @@ -4800,40 +4829,39 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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)) @@ -5232,7 +5260,6 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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); @@ -5241,13 +5268,25 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. getMessagesController().getStoriesController().toggleHidden(dialogId, hide, true, true); }; CharSequence str; - if (isArchive()) { - str = AndroidUtilities.replaceTags(LocaleController.formatString("StoriesMovedToDialogs", R.string.StoriesMovedToDialogs, ContactsController.formatName(user.first_name, null, 15))); + String name; + TLObject object; + if (dialogId >= 0) { + TLRPC.User user = getMessagesController().getUser(dialogId); + name = ContactsController.formatName(user.first_name, null, 15); + object = user; } else { - str = AndroidUtilities.replaceTags(LocaleController.formatString("StoriesMovedToContacts", R.string.StoriesMovedToContacts, ContactsController.formatName(user.first_name, null, 15))); + TLRPC.Chat chat = getMessagesController().getChat(-dialogId); + name = chat.title; + object = chat; + } + + if (isArchive()) { + str = AndroidUtilities.replaceTags(LocaleController.formatString("StoriesMovedToDialogs", R.string.StoriesMovedToDialogs, name)); + } else { + 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(); @@ -5284,6 +5323,9 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. if (dialogsHintCell != null && dialogsHintCell.getVisibility() == View.VISIBLE) { h += dialogsHintCell.getMeasuredHeight(); } + if (authHintCell != null && authHintCellVisible) { + h += authHintCell.getMeasuredHeight(); + } return h; } @@ -5437,11 +5479,67 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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 -> { @@ -5460,6 +5558,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. ), LocaleController.getString(R.string.RestorePremiumHintMessage) ); + updateAuthHintCellVisibility(false); } else if (isPremiumHintVisible()) { dialogsHintCellVisible = true; dialogsHintCell.setVisibility(View.VISIBLE); @@ -5479,6 +5578,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. ), LocaleController.getString(isPremiumHintUpgrade ? R.string.UpgradePremiumMessage : R.string.SaveOnAnnualPremiumMessage) ); + updateAuthHintCellVisibility(false); } else if (isCacheHintVisible()) { dialogsHintCellVisible = true; dialogsHintCell.setVisibility(View.VISIBLE); @@ -5498,9 +5598,11 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. ), LocaleController.getString(R.string.ClearStorageHintMessage) ); + updateAuthHintCellVisibility(false); } else { dialogsHintCellVisible = false; dialogsHintCell.setVisibility(View.GONE); + updateAuthHintCellVisibility(false); } } @@ -5778,6 +5880,14 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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) { @@ -6057,7 +6167,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } 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) { @@ -6530,6 +6640,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. (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) ); } @@ -7162,6 +7273,16 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } } } + 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) { @@ -8248,8 +8369,10 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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) { @@ -8419,7 +8542,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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; @@ -9245,6 +9368,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. viewPages[i].listView.requestLayout(); } } + dialogStoriesCell.setProgressToCollapse(1f, false); fragmentView.requestLayout(); } } @@ -10054,6 +10178,8 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. updateStoriesVisibility(wasDrawn); } else if (id == NotificationCenter.storiesEnabledUpdate) { updateStoriesPosting(); + } else if (id == NotificationCenter.unconfirmedAuthUpdate) { + updateDialogsHint(); } } @@ -11562,6 +11688,9 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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 863bfe57e..750c78fb5 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.HapticFeedbackConstants; 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.ConnectionsManager; 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; @@ -43,6 +49,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; @@ -84,6 +91,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; @@ -94,19 +105,43 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe 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() { @@ -157,6 +192,9 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe checkStickerPack(); } } else if (id == NotificationCenter.onEmojiInteractionsReceived) { + 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)) { @@ -216,7 +254,7 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe } } - if (bestView != null) { + if (bestView != null && chatActivity != null) { chatActivity.restartSticker(bestView); if (!EmojiData.hasEmojiSupportVibration(bestView.getMessageObject().getStickerEmoji()) && !bestView.getMessageObject().isPremiumSticker() && !bestView.getMessageObject().isAnimatedAnimatedEmoji()) { bestView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); @@ -229,88 +267,125 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe 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); - } else { - viewX += -AndroidUtilities.dp(24); - } - viewY -= photoImage.getImageWidth(); - drawingObject.lastX = viewX; - drawingObject.lastY = viewY; + 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(); } - drawingObject.lastW = photoImage.getImageWidth(); - drawingObject.lastH = photoImage.getImageHeight(); - break; + 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); + } else { + viewX += -AndroidUtilities.dp(24); + } + viewY -= photoImage.getImageWidth(); + drawingObject.lastX = viewX; + drawingObject.lastY = viewY; + } + 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.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.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.viewFound || childY + drawingObject.lastH < chatActivity.getChatListViewPadding() || childY > listView.getMeasuredHeight() - chatActivity.blurredViewBottomOffset) { - drawingObject.removing = true; - } + 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.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 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); + 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.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); + 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; @@ -423,7 +498,7 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe 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; @@ -442,8 +517,10 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe 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; } @@ -465,15 +542,15 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe 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; } @@ -484,7 +561,7 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe 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) { @@ -509,21 +586,28 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe } 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++; } } @@ -557,7 +641,7 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe 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) { @@ -589,20 +673,21 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe } 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 + "_"); @@ -610,11 +695,13 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe 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()) { - view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + contentLayout.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); } } }); @@ -622,7 +709,7 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe 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; @@ -647,13 +734,13 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe 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(); @@ -684,7 +771,20 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe 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; } @@ -790,11 +890,33 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe } 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 { @@ -806,6 +928,9 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe public float randomOffsetX; public float randomOffsetY; public boolean isPremiumSticker; + public boolean isReaction; + public AnimatedEmojiEffect genericEffect; + public long documentId; boolean wasPlayed; boolean isOut; boolean removing; @@ -813,6 +938,11 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe 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 76cb37b90..cb6800d15 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ExternalActionActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ExternalActionActivity.java @@ -89,7 +89,7 @@ public class ExternalActionActivity extends Activity implements INavigationLayou 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 5c9d2f723..8208745f6 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 class FilterChatlistActivity extends BaseFragment { ((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 @@ public class FilterChatlistActivity extends BaseFragment { 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 560082161..f3ccd3524 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/FilterCreateActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/FilterCreateActivity.java @@ -507,7 +507,7 @@ public class FilterCreateActivity extends BaseFragment { 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; } @@ -1995,6 +1995,13 @@ public class FilterCreateActivity extends BaseFragment { } } + 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; } @@ -2460,23 +2467,23 @@ public class FilterCreateActivity extends BaseFragment { 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 78135bcfc..f655ebc54 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/FiltersSetupActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/FiltersSetupActivity.java @@ -608,7 +608,7 @@ public class FiltersSetupActivity extends BaseFragment implements NotificationCe 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)); } @@ -618,7 +618,7 @@ public class FiltersSetupActivity extends BaseFragment implements NotificationCe 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()); } @@ -786,7 +786,7 @@ public class FiltersSetupActivity extends BaseFragment implements NotificationCe 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 822888e8e..cd684e37c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java @@ -29,6 +29,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Vibrator; import android.text.InputFilter; +import android.text.TextUtils; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -268,6 +269,61 @@ public class GroupCreateFinalActivity extends BaseFragment implements Notificati 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) { @@ -534,6 +590,7 @@ public class GroupCreateFinalActivity extends BaseFragment implements Notificati 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 d1c6e84dd..251974705 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.Activity; 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; @@ -51,7 +52,6 @@ import android.text.TextPaint; 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; @@ -185,6 +185,7 @@ import org.telegram.ui.Components.TermsOfServiceView; 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; @@ -377,7 +378,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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) { @@ -529,13 +530,26 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati } 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)); @@ -727,6 +741,14 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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); @@ -921,6 +943,14 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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()) { @@ -1373,6 +1403,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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); @@ -1393,6 +1424,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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); @@ -2776,17 +2808,20 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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; } @@ -3646,39 +3681,58 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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(); + if (user.bot_attach_menu) { + 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) { + processAttachMenuBot(intentAccount, peerId, attachMenuBotChoose, user, setAsAttachBot); + if (progress != null) { + progress.end(); } - }); - } - }); + 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(); + + 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(); + } + }; + + if (botApp.inactive || forceNotInternalForApps) { + AlertsCreator.createBotLaunchAlert(lastFragment, botApp, user, allowWrite, loadBotSheet); + } else { + loadBotSheet.run(); + } + }); + } + }); + } return; } } @@ -3687,133 +3741,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati TLRPC.User user = MessagesController.getInstance(intentAccount).getUser(peerId); if (user != null && user.bot) { if (user.bot_attach_menu) { - 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); - } 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); - } - builder.show(); - } - } else { - BulletinFactory.of(mainFragmentsStack.get(mainFragmentsStack.size() - 1)).createErrorBulletin(LocaleController.getString(R.string.BotCantAddToAttachMenu)).show(); - } - })); + processAttachMenuBot(intentAccount, peerId, attachMenuBotChoose, user, setAsAttachBot); } else { BulletinFactory.of(mainFragmentsStack.get(mainFragmentsStack.size() - 1)).createErrorBulletin(LocaleController.getString(R.string.BotCantAddToAttachMenu)).show(); } @@ -4728,6 +4656,115 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati } } + 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(); @@ -5410,6 +5447,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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); @@ -5620,6 +5658,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati if (VoIPFragment.getInstance() != null) { VoIPFragment.onPause(); } + SpoilerEffect2.pause(true); } @Override @@ -5793,6 +5832,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati VoIPFragment.onResume(); } invalidateTabletMode(); + SpoilerEffect2.pause(false); } private void invalidateTabletMode() { @@ -5897,6 +5937,8 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati } } 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) { @@ -6402,7 +6444,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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) { @@ -7426,6 +7468,18 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati } } + 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(); @@ -7449,6 +7503,28 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati } } + 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 c629419dc..9309c13b2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java @@ -1592,7 +1592,7 @@ public class LocationActivity extends BaseFragment implements NotificationCenter } 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) { @@ -1915,6 +1915,9 @@ public class LocationActivity extends BaseFragment implements NotificationCenter } private boolean checkGpsEnabled() { + if (disablePermissionCheck()) { + return false; + } if (!getParentActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS)) { return true; } @@ -2707,7 +2710,9 @@ public class LocationActivity extends BaseFragment implements NotificationCenter } } 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; @@ -2722,6 +2727,10 @@ public class LocationActivity extends BaseFragment implements NotificationCenter } } + 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/LogoutActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LogoutActivity.java index 0ba4bbaae..d40575165 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LogoutActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LogoutActivity.java @@ -136,7 +136,7 @@ public class LogoutActivity extends BaseFragment { if (freeAccounts > 0 && availableAccount != null) { presentFragment(new LoginActivity(availableAccount)); } else if (!UserConfig.hasPremiumOnAccounts()) { - LimitReachedBottomSheet limitReachedBottomSheet = new LimitReachedBottomSheet(this, getContext(), TYPE_ACCOUNTS, currentAccount); + LimitReachedBottomSheet limitReachedBottomSheet = new LimitReachedBottomSheet(this, getContext(), TYPE_ACCOUNTS, currentAccount, null); showDialog(limitReachedBottomSheet); } } else if (position == passcodeRow) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/MessageSeenView.java b/TMessagesProj/src/main/java/org/telegram/ui/MessageSeenView.java index 1b579e56c..c7adda3a6 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.HideViewAfterAnimation; 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 @@ public class MessageSeenView extends FrameLayout { SimpleTextView nameView; TextView readView; AvatarDrawable avatarDrawable = new AvatarDrawable(); - AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable rightDrawable; + StatusBadgeComponent statusBadgeComponent; TLObject object; @@ -379,7 +380,7 @@ public class MessageSeenView extends FrameLayout { 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 class MessageSeenView extends FrameLayout { } 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 704c57bd1..a20feb243 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.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Insets; +import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Outline; import android.graphics.Paint; @@ -37,8 +42,10 @@ 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.SurfaceTexture; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; @@ -55,7 +62,6 @@ import android.text.Layout; 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.TransitionManager; 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.FrameLayout; 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.collection.LongSparseArray; 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; @@ -138,6 +146,7 @@ import org.telegram.messenger.AndroidUtilities; 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; @@ -186,7 +195,6 @@ 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.Adapters.MentionsAdapter; import org.telegram.ui.Cells.CheckBoxCell; import org.telegram.ui.Cells.PhotoPickerPhotoCell; import org.telegram.ui.Cells.TextSelectionHelper; @@ -198,8 +206,10 @@ import org.telegram.ui.Components.AnimatedFloat; import org.telegram.ui.Components.AnimatedTextView; import org.telegram.ui.Components.AnimationProperties; import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.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; @@ -220,14 +230,12 @@ import org.telegram.ui.Components.LinkPath; 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.PhotoViewerCaptionEnterView; import org.telegram.ui.Components.PhotoViewerWebView; import org.telegram.ui.Components.PickerBottomLayoutViewer; import org.telegram.ui.Components.PipVideoOverlay; @@ -236,6 +244,7 @@ import org.telegram.ui.Components.Premium.LimitReachedBottomSheet; 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; @@ -244,8 +253,8 @@ import org.telegram.ui.Components.TextViewSwitcher; 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; @@ -255,6 +264,7 @@ import org.telegram.ui.Components.VideoTimelinePlayView; 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; @@ -277,6 +287,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; @@ -306,6 +318,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; @@ -315,6 +329,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat return isVisibleOrAnimating; } + public SurfaceView getVideoSurfaceView() { + return videoSurfaceView; + } + private static class PhotoViewerActionBarContainer extends FrameLayout implements NotificationCenter.NotificationCenterDelegate { private FrameLayout container; @@ -326,12 +344,18 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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)); @@ -342,20 +366,26 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -404,7 +434,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -457,15 +487,15 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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); @@ -473,9 +503,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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); @@ -582,21 +612,21 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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"); @@ -604,7 +634,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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); @@ -678,8 +708,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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, @@ -698,20 +728,20 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); @@ -725,7 +755,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) ); } } @@ -748,6 +778,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -786,11 +818,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -811,8 +841,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private float[] pressedDrawableAlpha = new float[2]; private int touchSlop; - private boolean useSmoothKeyboard; - private VideoForwardDrawable videoForwardDrawable; private AnimatorSet currentListViewAnimation; @@ -823,13 +851,18 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private MediaController.CropState leftCropState; private MediaController.CropState rightCropState; private PhotoFilterView photoFilterView; - private IPhotoPaintView photoPaintView; + 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; @@ -846,7 +879,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private boolean pipAvailable; - private Object lastInsets; + private final Rect insets = new Rect(); private boolean padImageForHorizontalInsets; private boolean doneButtonPressed; @@ -888,6 +921,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private View flashView; private AnimatorSet flashAnimator; private TextureView videoTextureView; + private SurfaceView videoSurfaceView; + private boolean usedSurfaceView; private FirstFrameView firstFrameView; private VideoPlayer videoPlayer; private boolean manuallyPaused; @@ -1019,7 +1054,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } }; - private TextView captionLimitView; private Drawable pickerViewSendDrawable; private CharSequence customTitle; public boolean skipLastFrameDraw; @@ -1298,7 +1332,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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())); @@ -1339,7 +1373,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat bufferedProgress = -1; } } - if (!inPreview && videoTimelineView.getVisibility() == View.VISIBLE) { + if (!inPreview && videoTimelineViewContainer.getVisibility() == View.VISIBLE) { if (progress >= videoTimelineView.getRightProgress()) { manuallyPaused = false; pauseVideoOrWeb(); @@ -1420,38 +1454,64 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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); + 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); + } } + } }; @@ -1487,53 +1547,173 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat @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; @@ -1602,7 +1782,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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); @@ -1620,6 +1800,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -1627,6 +1810,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); @@ -1674,6 +1859,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private float dragY; private float translationX; private float translationY; + private float translateY; private float scale = 1; private float rotate = 0; private float mirror = 0; @@ -1720,19 +1906,17 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private boolean shownControlsByEnd = false; private boolean actionBarWasShownBeforeByEnd = false; - 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; @@ -1829,7 +2013,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -1876,7 +2060,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat setWillNotDraw(false); setClipToPadding(false); - setTranslationY(-AndroidUtilities.dp(10)); + setTranslationY(-dp(10)); DefaultItemAnimator defaultItemAnimator; setItemAnimator(defaultItemAnimator = new DefaultItemAnimator() { @Override @@ -1886,7 +2070,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat }); 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(); @@ -1898,8 +2082,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -1910,8 +2094,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); } } } @@ -1931,13 +2115,13 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -1966,7 +2150,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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(); @@ -2004,27 +2188,27 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat @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); } } } @@ -2040,7 +2224,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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]; @@ -2057,7 +2241,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -2229,7 +2413,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; } @@ -2274,7 +2458,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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 { @@ -2549,127 +2733,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -2699,10 +2762,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -2737,7 +2797,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -2747,8 +2807,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); @@ -2758,17 +2819,18 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -2797,7 +2859,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -2854,33 +2917,39 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); } @@ -2902,8 +2971,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat // 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(); @@ -2944,79 +3013,29 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } - @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; @@ -3039,17 +3058,24 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat @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; } @@ -3059,7 +3085,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - adjustPanLayoutHelper.onDetach(); Bulletin.removeDelegate(this); } @@ -3067,7 +3092,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); } } } @@ -3120,12 +3145,12 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat .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) { @@ -3138,7 +3163,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -3171,14 +3196,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -3208,7 +3233,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -3285,15 +3310,25 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } - 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); @@ -3302,7 +3337,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat public void setVisibility(int visibility, boolean withScrollView) { super.setVisibility(visibility); if (inScrollView && withScrollView) { - captionScrollView.setVisibility(visibility); + scrollView.setVisibility(visibility); } } @@ -3310,7 +3345,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat public void setAlpha(float alpha) { this.alpha = alpha; if (inScrollView) { - captionScrollView.setAlpha(alpha); + scrollView.setAlpha(alpha); } else { super.setAlpha(alpha); } @@ -3329,17 +3364,17 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); } } @@ -3349,13 +3384,13 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -3373,17 +3408,23 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -3393,6 +3434,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -3502,7 +3547,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } final int lineHeight = textView.getPaint().getFontMetricsInt(null); - return height - lineHeight * i - AndroidUtilities.dp(8); + return height - lineHeight * i - dp(8); } public void reset() { @@ -3550,6 +3595,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat consumed[1] += dy; } } + onScrollUpdate(); captionTextViewSwitcher.setTranslationY(overScrollY); return true; @@ -3588,6 +3634,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat captionTextViewSwitcher.setTranslationY(overScrollY); } } + + onScrollUpdate(); } } @@ -3604,6 +3652,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat springAnimation.cancel(); nestedScrollStarted = true; overScrollY = captionTextViewSwitcher.getTranslationY(); + onScrollStart(); } return true; } @@ -3614,6 +3663,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (!nestedScrollStarted && overScrollY != 0 && scroller != null && scroller.isFinished()) { startSpringAnimationIfNotRunning(0); } + onScrollUpdate(); } @Override @@ -3623,9 +3673,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -3652,41 +3707,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; } } @@ -4255,8 +4277,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); @@ -4280,8 +4302,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat windowView = new FrameLayout(activity) { - private Runnable attachRunnable; - @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return isVisible && super.onInterceptTouchEvent(ev); @@ -4334,8 +4354,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -4343,21 +4362,22 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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; } @@ -4391,14 +4411,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -4437,7 +4457,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (textSelectionHelper.isInSelectionMode()) { textSelectionHelper.clear(); } - if (captionEditText.isPopupShowing() || captionEditText.isKeyboardVisible()) { + if (isCaptionOpen()) { closeCaptionEnter(true); return false; } @@ -4449,10 +4469,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat @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); } } @@ -4484,13 +4503,18 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -4540,18 +4564,33 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); @@ -4562,23 +4601,23 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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); @@ -4638,7 +4677,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (photoPaintView != null && photoPaintView.onBackPressed()) { return; } - if (needCaptionLayout && (captionEditText.isPopupShowing() || captionEditText.isKeyboardVisible())) { + if (isCaptionOpen()) { closeCaptionEnter(false); return; } @@ -5003,7 +5042,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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; @@ -5449,7 +5488,12 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } }); - 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)); @@ -5462,7 +5506,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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 @@ -5569,8 +5613,15 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat }); } + 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); @@ -5612,92 +5663,12 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat }; 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)); - -// 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); @@ -5709,7 +5680,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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)); @@ -5742,7 +5713,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat }); 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)); @@ -5751,43 +5722,47 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -5797,16 +5772,19 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat @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); } } @@ -5814,7 +5792,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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()); } @@ -5864,6 +5842,31 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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() { @@ -5998,9 +6001,109 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } }); + 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)); + + captionEdit = new CaptionPhotoViewer(containerView.getContext(), windowView, containerView, containerView, resourcesProvider, blurManager, this::applyCaption) { + private final Path path = new Path(); + + @Override + protected boolean customBlur() { + return true; + } + + @Override + protected boolean ignoreTouches() { + return !keyboardShown && currentEditMode != EDIT_MODE_NONE; + } + + @Override + 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 { + canvas.translate(ox, oy); + } + drawCaptionBlur(canvas, blur, text ? 0xFF787878 : 0xFF262626, thisView ? (text ? 0 : 0x33000000) : 0x44000000, false, !text, !text && thisView); + canvas.restore(); + } + + @Override + protected boolean captionLimitToast() { + if (limitBulletin != null && Bulletin.getVisibleBulletin() == limitBulletin) { + return false; + } + return showCaptionLimitBulletin(containerView); + } + + @Override + 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); @@ -6011,55 +6114,20 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { - @Override - public boolean dispatchTouchEvent(MotionEvent ev) { - return bottomTouchEnabled && super.dispatchTouchEvent(ev); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - return bottomTouchEnabled && super.onTouchEvent(event); - } - - @Override - public void setVisibility(int visibility) { - super.setVisibility(visibility); - if (captionEditText.getCaptionLimitOffset() < 0) { - captionLimitView.setVisibility(visibility); - } else { - captionLimitView.setVisibility(View.GONE); - } - } - - @Override - public void setTranslationY(float translationY) { - super.setTranslationY(translationY); - captionLimitView.setTranslationY(translationY); - } - - @Override - public void setAlpha(float alpha) { - super.setAlpha(alpha); - captionLimitView.setAlpha(alpha); - } - }; + 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.MULTIPLY)); - pickerViewSendButton.setImageResource(R.drawable.attach_send); + pickerViewSendButton.setImageResource(R.drawable.msg_input_send_mini); pickerViewSendButton.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_dialogFloatingIcon), PorterDuff.Mode.MULTIPLY)); - containerView.addView(pickerViewSendButton, LayoutHelper.createFrame(56, 56, Gravity.RIGHT | Gravity.BOTTOM, 0, 0, 14, 14)); + 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 { - 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; @@ -6078,7 +6146,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (!isStoryViewer && (parentChatActivity == null || parentChatActivity.isInScheduleMode())) { return false; } - if (captionEditText.getCaptionLimitOffset() < 0) { + if (captionEdit.isCaptionOverLimit()) { return false; } TLRPC.Chat chat = parentChatActivity.getCurrentChat(); @@ -6141,7 +6209,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; } ActionBarMenuSubItem cell = new ActionBarMenuSubItem(parentActivity, a == 0, a == 3, resourcesProvider); @@ -6164,7 +6232,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat cell.setTextAndIcon(LocaleController.getString(R.string.SendAsFile), R.drawable.msg_sendfile); } } - cell.setMinimumWidth(AndroidUtilities.dp(196)); + cell.setMinimumWidth(dp(196)); cell.setColors(0xffffffff, 0xffffffff); sendPopupLayout.addView(cell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); cell.setOnClickListener(v -> { @@ -6198,24 +6266,17 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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)); + sendPopupWindow.showAtLocation(view, Gravity.LEFT | Gravity.TOP, location[0] + view.getMeasuredWidth() - sendPopupLayout.getMeasuredWidth() + dp(14), location[1] - sendPopupLayout.getMeasuredHeight() - dp(18)); 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; @@ -6235,7 +6296,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -6244,7 +6305,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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; } @@ -6266,11 +6327,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -6300,7 +6361,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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()); @@ -6308,11 +6369,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -6337,7 +6398,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -6353,27 +6414,39 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } }); - 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) { + compressItem = new VideoCompressButton(parentActivity); + compressItem.setTag(1); + compressItem.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); + + selectedCompression = selectCompression(); + 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 (isCaptionOpen() || muteVideo) { return; } - placeProvider.needAddMorePhotos(); - closePhoto(true, false); + if (compressItem.getTag() == null) { + if (videoConvertSupported) { + if (tooltip == null) { + tooltip = new Tooltip(activity, containerView, 0xcc111111, Color.WHITE); + } + tooltip.setText(LocaleController.getString("VideoQualityIsTooLow", R.string.VideoQualityIsTooLow)); + tooltip.show(compressItem); + } + return; + } + showQualityView(true); + requestVideoPreview(1); }); tuneItem = new ImageView(parentActivity); tuneItem.setScaleType(ImageView.ScaleType.CENTER); - tuneItem.setImageResource(R.drawable.msg_photo_settings); + 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 (captionEditText.getTag() != null) { + if (isCaptionOpen()) { return; } if (isCurrentVideo) { @@ -6393,191 +6466,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat }); tuneItem.setContentDescription(LocaleController.getString("AccDescrPhotoAdjust", R.string.AccDescrPhotoAdjust)); - compressItem = new ImageView(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.setContentDescription(LocaleController.getString("AccDescrVideoQuality", R.string.AccDescrVideoQuality)); - itemsLayout.addView(compressItem, LayoutHelper.createLinear(48, 48)); - compressItem.setOnClickListener(v -> { - if (captionEditText.getTag() != null || muteVideo) { - return; - } - if (compressItem.getTag() == null) { - if (videoConvertSupported) { - if (tooltip == null) { - tooltip = new Tooltip(activity, containerView, 0xcc111111, Color.WHITE); - } - tooltip.setText(LocaleController.getString("VideoQualityIsTooLow", R.string.VideoQualityIsTooLow)); - tooltip.show(compressItem); - } - return; - } - showQualityView(true); - 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) { - 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); - } - }); - 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()); - } - } - } - }; - 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.commit(); - 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.MULTIPLY) : null); - if (!checkImageView.isChecked()) { - checkImageView.callOnClick(); - } - }); - - 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); - }); - editorDoneLayout = new PickerBottomLayoutViewer(activityContext); editorDoneLayout.setBackgroundColor(0xcc000000); editorDoneLayout.updateSelectedCount(0, false); @@ -6630,7 +6518,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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)); @@ -6686,16 +6574,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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)); @@ -6703,7 +6586,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat ((FrameLayout.LayoutParams) checkImageView.getLayoutParams()).topMargin += AndroidUtilities.statusBarHeight; } checkImageView.setOnClickListener(v -> { - if (captionEditText.getTag() != null) { + if (isCaptionOpen()) { return; } setPhotoChecked(); @@ -6715,7 +6598,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat ((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); @@ -6755,343 +6638,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - try { - return !bottomTouchEnabled && super.onInterceptTouchEvent(ev); - } catch (Exception e) { - FileLog.e(e); - } - return false; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (bottomTouchEnabled && event.getAction() == MotionEvent.ACTION_DOWN) { - keyboardAnimationEnabled = true; - } - return !bottomTouchEnabled && super.onTouchEvent(event); - } - - @Override - protected void extendActionMode(ActionMode actionMode, Menu menu) { - if (parentChatActivity != null) { - parentChatActivity.extendActionMode(menu); - } - } - }; - captionEditText.setDelegate(new PhotoViewerCaptionEnterView.PhotoViewerCaptionEnterViewDelegate() { - @Override - public void onCaptionEnter() { - closeCaptionEnter(true); - } - - @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); - if (object instanceof String) { - AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity, resourcesProvider); - builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); - 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; - } - return false; - }); - hintView = new UndoView(activityContext, null, false, resourcesProvider); - hintView.setAdditionalTranslationY(AndroidUtilities.dp(112)); + 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)); @@ -7105,12 +6653,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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()); @@ -7122,11 +6664,12 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat textSelectionHelper.setInvalidateParent(); } - public void showCaptionLimitBulletin(FrameLayout view) { + private Bulletin limitBulletin; + public boolean showCaptionLimitBulletin(FrameLayout view) { if (!(parentFragment instanceof ChatActivity) || !ChatObject.isChannelAndNotMegaGroup(((ChatActivity) parentFragment).getCurrentChat())) { - return; + return false; } - BulletinFactory.of(view, resourcesProvider).createCaptionLimitBulletin(MessagesController.getInstance(currentAccount).captionLengthLimitPremium, ()->{ + limitBulletin = BulletinFactory.of(view, resourcesProvider).createCaptionLimitBulletin(MessagesController.getInstance(currentAccount).captionLengthLimitPremium, ()->{ closePhoto(false, false); if (parentAlert != null) { parentAlert.dismiss(true); @@ -7134,7 +6677,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (parentFragment != null) { parentFragment.presentFragment(new PremiumPreviewFragment("caption_limit")); } + }).setOnHideListener(() -> { + limitBulletin = null; }).show(); + return true; } public ChatAttachAlert getParentAlert() { @@ -7196,7 +6742,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } private void sendPressed(boolean notify, int scheduleDate, boolean replace, boolean forceDocument, boolean confirmed) { - if (captionEditText.getTag() != null) { + if (isCaptionOpen()) { return; } if (placeProvider != null && !doneButtonPressed) { @@ -7209,7 +6755,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -7318,7 +6864,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -7335,6 +6881,13 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } + private boolean isCaptionOpen() { + return ( + captionEdit != null && + (captionEdit.keyboardNotifier.keyboardVisible() || captionEdit.editText.isPopupShowing()) + ); + } + private void showShareAlert(ArrayList messages) { final FrameLayout photoContainerView = containerView; requestAdjustToNothing(); @@ -7395,10 +6948,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } 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); } @@ -7482,9 +7035,27 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat }).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); @@ -7492,17 +7063,12 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -7510,7 +7076,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } if (textSelectionHelper != null && getStaticTextLayout() != null) { textSelectionHelper.setSelectabeleView(this); - textSelectionHelper.setScrollingParent(captionScrollView); + textSelectionHelper.setScrollingParent(scrollView); textSelectionHelper.update(getPaddingLeft(), getPaddingTop()); textSelectionHelper.onTouchEvent(event); } @@ -7543,7 +7109,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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()); @@ -7553,7 +7119,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -7563,9 +7129,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat pressedLink = null; linkResult = true; } - - boolean b = linkResult || super.onTouchEvent(event); - return bottomTouchEnabled && b; + return linkResult || super.onTouchEvent(event); } @Override @@ -7674,8 +7238,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -7701,17 +7265,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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; + return insets.right; } private void dismissInternal() { @@ -7750,10 +7308,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { + 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); } @@ -7780,21 +7342,38 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); + 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); } @@ -7815,11 +7394,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -7836,18 +7415,16 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); } }); @@ -7858,8 +7435,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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 ); @@ -7875,10 +7452,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); } @@ -8041,12 +7616,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -8058,7 +7632,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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()); @@ -8067,26 +7641,26 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -8113,8 +7687,24 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; + } } } @@ -8174,9 +7764,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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) { @@ -8217,7 +7807,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat @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(); @@ -8274,7 +7864,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -8327,28 +7917,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat }); } - 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) { @@ -8523,52 +8091,59 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; - } - - 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; - } - - if (captionEditText.getFieldCharSequence().length() != 0 && !placeProvider.isPhotoChecked(currentIndex)) { - setPhotoChecked(); - } - if (placeProvider != null) { - placeProvider.onApplyCaption(caption); - } - setCurrentCaption(null, result[0], false, false); + CharSequence cs = applyCaption(); +// if (cs != null) { +// setCurrentCaption(null, cs, false, false); +// } } - captionEditText.setTag(null); - if (isCurrentVideo && customTitle == null) { - actionBarContainer.setTitleAnimated(lastTitle, true, true); - actionBarContainer.setSubtitle(muteVideo ? LocaleController.getString("SoundMuted", R.string.SoundMuted) : currentSubtitle); +// updateCaptionTextForCurrentPhoto(object); + if (captionEdit.editText.isPopupShowing()) { + captionEdit.editText.hidePopup(true); } - updateCaptionTextForCurrentPhoto(object); - if (captionEditText.isPopupShowing()) { - captionEditText.hidePopup(); + captionEdit.editText.closeKeyboard(); +// if (Build.VERSION.SDK_INT >= 19) { +// captionEditText.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); +// } + } + + private CharSequence applyCaption() { + if (!isVisible() || placeProvider == null || currentIndex < 0 || currentIndex >= imagesArrLocals.size()) { + return null; } - captionEditText.closeKeyboard(); - if (Build.VERSION.SDK_INT >= 19) { - captionEditText.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); + Object object = imagesArrLocals.get(currentIndex); + + CharSequence caption = captionEdit.getText(); + CharSequence[] result = new CharSequence[] { caption }; + + if (hasCaptionForAllMedia && !TextUtils.equals(captionForAllMedia, caption) && placeProvider.getPhotoIndex(currentIndex) != 0 && placeProvider.getSelectedCount() > 0) { + hasCaptionForAllMedia = false; } + + 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; + } + + if (caption.length() != 0 && !placeProvider.isPhotoChecked(currentIndex)) { + setPhotoChecked(); + } + if (placeProvider != null) { + placeProvider.onApplyCaption(caption); + } + return result[0]; } private void updateVideoPlayerTime() { @@ -8580,7 +8155,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -8599,7 +8174,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -8615,23 +8190,46 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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; @@ -8668,9 +8266,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat checkImageView.setColor(getThemedColor(Theme.key_dialogFloatingButton), 0xffffffff); } PorterDuffColorFilter filter = new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY); - if (timeItem != null && timeItem.getColorFilter() != null) { - timeItem.setColorFilter(filter); - } if (paintItem != null && paintItem.getColorFilter() != null) { paintItem.setColorFilter(filter); } @@ -8698,8 +8293,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (photoFilterView != null) { photoFilterView.updateColors(); } - if (captionEditText != null) { - captionEditText.updateColors(); + if (captionEdit != null) { + captionEdit.updateColors(resourcesProvider); } if (videoTimelineView != null) { videoTimelineView.invalidate(); @@ -8891,7 +8486,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -8908,7 +8503,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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); @@ -9028,6 +8623,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat captureFrameReadyAtTime = -1; captureFrameAtTime = -1; needCaptureFrameReadyAtTime = -1; + firstFrameRendered = false; if (videoPlayer == null) { if (injectingVideoPlayer != null) { videoPlayer = injectingVideoPlayer; @@ -9057,18 +8653,48 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); } @@ -9158,98 +8784,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat @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) { @@ -9262,7 +8797,13 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); } @@ -9273,7 +8814,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (newPlayerCreated) { seekToProgressPending = seekToProgressPending2; videoPlayerSeekbar.setProgress(0); - videoTimelineView.setProgress(0); + videoTimelineView.setProgress(videoTimelineView.getLeftProgress()); videoPlayerSeekbar.setBufferedProgress(0); if (currentMessageObject != null) { @@ -9327,7 +8868,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -9398,7 +8939,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -9415,11 +8956,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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)); } } @@ -9440,7 +8981,12 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); @@ -9451,18 +8997,25 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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))); } @@ -9474,10 +9027,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -9494,6 +9051,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } private void releasePlayer(boolean onClose) { + usedSurfaceView = false; if (videoPlayer != null) { cancelVideoPlayRunnable(); AndroidUtilities.cancelRunOnUIThread(setLoadingRunnable); @@ -9552,6 +9110,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } videoTextureView = null; } + blurManager.resetBitmap(); + if (videoSurfaceView != null) { + videoSurfaceView = null; + } if (isPlaying) { isPlaying = false; AndroidUtilities.cancelRunOnUIThread(updateProgressRunnable); @@ -9623,11 +9185,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -10121,6 +9683,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (setImage) { centerImage.setImageBitmap(bitmap); centerImage.setOrientation(0, true); + centerBlur.destroy(); containerView.requestLayout(); } ignoreDidSetImage = false; @@ -10181,7 +9744,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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 @@ -10232,7 +9795,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat @Override public int getVideoThumbX() { - return (int) (AndroidUtilities.dp(16) + (videoTimelineView.getMeasuredWidth() - AndroidUtilities.dp(32)) * avatarStartProgress); + return (int) (dp(16) + (videoTimelineView.getMeasuredWidth() - dp(32)) * avatarStartProgress); } }); } @@ -10264,51 +9827,51 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); - } - } - }); +// 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); @@ -10321,9 +9884,12 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -10403,20 +9969,20 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -10434,7 +10000,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -10449,13 +10015,13 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); @@ -10497,6 +10063,13 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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) { @@ -10521,6 +10094,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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)); @@ -10534,10 +10108,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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)); @@ -10596,12 +10166,13 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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)); @@ -10610,9 +10181,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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)); } @@ -10628,11 +10196,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -10676,14 +10243,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) ); @@ -10702,6 +10269,13 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -10752,7 +10326,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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(); @@ -10773,13 +10347,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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)); @@ -10790,9 +10365,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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)); } @@ -10807,14 +10379,13 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -10835,10 +10406,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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 { @@ -10855,7 +10426,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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() { @@ -10869,6 +10440,13 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -10889,12 +10467,13 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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)); @@ -10905,9 +10484,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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)); } @@ -10954,6 +10530,36 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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) { @@ -10969,6 +10575,28 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } + @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) { @@ -10991,29 +10619,49 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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() { photoPaintView.maybeShowDismissalAlert(PhotoViewer.this, parentActivity, () -> switchToEditMode(EDIT_MODE_NONE)); } 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; @@ -11025,6 +10673,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat photosCounterView.setVisibility(View.GONE); updateActionBarTitlePadding(); } + showEditCaption(false, true); Bitmap bitmap = centerImage.getBitmap(); final float finalScale = scale; @@ -11035,7 +10684,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat float oldScale; float newScale; if (sendPhotoType == SELECT_TYPE_AVATAR) { - animateToY = AndroidUtilities.dp(12); + animateToY = dp(12); if (photoPaintView != null) { animateToY += photoPaintView.getAdditionalTop() / 2f; } @@ -11045,7 +10694,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -11071,8 +10720,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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( @@ -11091,6 +10740,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -11120,6 +10774,12 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -11417,7 +11077,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -11436,7 +11096,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -11761,6 +11421,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -11781,6 +11445,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat avatarsArr.clear(); secureDocuments.clear(); imagesArrLocals.clear(); + if (blurManager != null) { + blurManager.resetBitmap(); + } if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { actionBar.setElevation(0); } @@ -11794,13 +11461,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -11811,8 +11471,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -11870,7 +11528,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -11878,10 +11536,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -12069,10 +11728,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -12088,14 +11744,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -12113,23 +11769,28 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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()) { @@ -12139,7 +11800,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat pickerViewSendButton.setTranslationY(0); pickerViewSendButton.setAlpha(1.0f); } - if (navigationBar != null) { + if (navigationBar != null && !fancyShadows) { navigationBar.setVisibility(View.VISIBLE); navigationBar.setAlpha(1.0f); } @@ -12194,9 +11855,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } else if (allowTimeItem && entry instanceof MediaController.SearchImage) { allowTimeItem = ((MediaController.SearchImage) entry).type == 0; } - if (allowTimeItem) { - timeItem.setVisibility(View.VISIBLE); - } + captionEdit.setTimerVisible(allowTimeItem, true); } checkFullscreenButton(); } @@ -12243,10 +11902,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -12554,6 +12213,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -12751,7 +12411,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat updateCaptionTextForCurrentPhoto(object); PorterDuffColorFilter filter = new PorterDuffColorFilter(getThemedColor(Theme.key_dialogFloatingButton), PorterDuff.Mode.MULTIPLY); - timeItem.setColorFilter(ttl != 0 ? filter : null); + captionEdit.setIsVideo(isVideo); + captionEdit.setTimer(ttl); paintItem.setColorFilter(isPainted ? filter : null); cropItem.setColorFilter(isCropped ? filter : null); tuneItem.setColorFilter(isFiltered ? filter : null); @@ -12761,6 +12422,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -12825,6 +12487,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -12861,7 +12527,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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 { @@ -12880,18 +12546,58 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat return message; } - private void showVideoTimeline(boolean show, boolean animated) { - if (!animated) { - videoTimelineView.animate().setListener(null).cancel(); - videoTimelineView.setVisibility(show ? View.VISIBLE : View.GONE); - videoTimelineView.setTranslationY(0); - videoTimelineView.setAlpha(pickerView.getAlpha()); + 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 && videoTimelineView.getTag() == null) { - if (videoTimelineView.getVisibility() != View.VISIBLE) { - videoTimelineView.setVisibility(View.VISIBLE); - videoTimelineView.setAlpha(pickerView.getAlpha()); - videoTimelineView.setTranslationY(AndroidUtilities.dp(58)); + 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) { + videoTimelineViewContainer.animate().setListener(null).cancel(); + videoTimelineViewContainer.setVisibility(show ? View.VISIBLE : View.GONE); + videoTimelineView.setTranslationY(0); + videoTimelineViewContainer.setAlpha(pickerView.getAlpha()); + } else { + 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(); @@ -12902,20 +12608,20 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -13212,6 +12918,18 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -13241,6 +12959,18 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -13281,7 +13011,15 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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) { @@ -13292,8 +13030,52 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } 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)); @@ -13482,9 +13264,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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 { @@ -13718,8 +13500,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } - 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) { @@ -14212,15 +13994,15 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); } @@ -14397,10 +14179,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); @@ -14419,6 +14201,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat animationEndRunnable = null; } animationInProgress = 0; + invalidateBlur(); } } return animationInProgress != 0; @@ -14467,7 +14250,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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 { @@ -14497,7 +14283,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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); @@ -14511,6 +14297,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat doneButtonPressed = false; closePhotoAfterSelect = true; allowShowFullscreenButton = true; + usedSurfaceView = false; parentChatActivity = chatActivity; lastTitle = null; isEmbedVideo = embedSeekTime != null; @@ -14663,8 +14450,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -14752,6 +14539,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat containerView.setLayerType(View.LAYER_TYPE_NONE, null); } animationInProgress = 0; + invalidateBlur(); transitionAnimationStartTime = 0; leftCropState = null; leftCropTransform.setViewTransform(false); @@ -14793,7 +14581,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); } @@ -14880,26 +14671,25 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat @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); @@ -14910,6 +14700,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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), @@ -14928,6 +14720,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); animationInProgress = 0; + invalidateBlur(); backgroundDrawable.setAlpha(255); containerView.invalidate(); pickerView.setTranslationY(0f); @@ -15013,7 +14806,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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); @@ -15024,26 +14817,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -15055,7 +14828,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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); @@ -15119,6 +14892,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat savedState = null; } currentEditMode = EDIT_MODE_NONE; + captionEdit.keyboardNotifier.ignore(false); + if (paintKeyboardNotifier != null) { + paintKeyboardNotifier.ignore(currentEditMode != EDIT_MODE_PAINT); + } } if (navigationBar != null) { @@ -15131,9 +14908,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -15171,13 +14948,17 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { + 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); + } } } } @@ -15198,7 +14979,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat containerView.removeView(photoViewerWebView); photoViewerWebView = null; } - captionEditText.onDestroy(); +// captionEdit.editText.onDestroy(); if (parentChatActivity != null && parentChatActivity.getFragmentView() != null) { parentChatActivity.getFragmentView().invalidate(); } @@ -15267,8 +15048,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -15350,7 +15131,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); } @@ -15386,6 +15170,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat animationEndRunnable = null; containerView.setLayerType(View.LAYER_TYPE_NONE, null); animationInProgress = 0; + invalidateBlur(); onPhotoClosed(object); MediaController.getInstance().tryResumePausedAudio(); }; @@ -15466,6 +15251,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat currentAnimation.removeSecondParentView(containerView); currentAnimation = null; centerImage.setImageBitmap((Drawable) null); + centerBlur.destroy(); } if (placeProvider != null && !placeProvider.canScrollAway()) { placeProvider.cancelButtonPressed(); @@ -15533,9 +15319,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat currentThumb = null; } animatingImageView.setImageBitmap(null); - if (captionEditText != null) { - captionEditText.onDestroy(); - } +// if (captionEdit.editText != null) { +// captionEdit.editText.onDestroy(); +// } if (this == PipInstance) { PipInstance = null; } else { @@ -15586,13 +15372,15 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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) { @@ -15677,7 +15465,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); @@ -15707,20 +15496,20 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; } @@ -15732,9 +15521,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; } @@ -15758,11 +15547,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } 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; } @@ -15797,7 +15586,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat return true; } - if (captionEditText.isPopupShowing() || captionEditText.isKeyboardVisible()) { + if (captionEdit.editText.isPopupShowing() || captionEdit.editText.isKeyboardVisible()) { if (ev.getAction() == MotionEvent.ACTION_UP) { closeCaptionEnter(true); } @@ -15905,6 +15694,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -15928,7 +15718,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -15947,7 +15737,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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; @@ -15980,6 +15770,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (scale != 1 || currentEditMode != EDIT_MODE_NONE) { translationY -= moveDy; } + invalidateBlur(); containerView.invalidate(); } } else { @@ -16072,11 +15863,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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; } @@ -16121,7 +15912,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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() { @@ -16130,7 +15921,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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() { @@ -16198,6 +15989,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat public void setAnimationValue(float value) { animationValue = value; containerView.invalidate(); + invalidateBlur(); } @Keep @@ -16249,8 +16041,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); } @@ -16269,6 +16061,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (scroller.getStartY() < maxY && scroller.getStartY() > minY) { translationY = scroller.getCurrY(); } + invalidateBlur(); containerView.invalidate(); } } @@ -16325,6 +16118,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (padImageForHorizontalInsets) { canvas.restore(); } + drawFancyShadows(canvas); return; } if (animationInProgress == 3 || !isVisible && animationInProgress != 2 && !pipAnimationInProgress) { @@ -16387,6 +16181,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (scroller.getStartY() < maxY && scroller.getStartY() > minY) { translationY = scroller.getCurrY(); } + invalidateBlur(); containerView.invalidate(); } } @@ -16415,6 +16210,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } + currentTranslationY += translateY; + if (currentEditMode == EDIT_MODE_PAINT) { + currentTranslationY += photoPaintView.getEmojiPadding(false) / 2f; + } + if (photoViewerWebView != null) { photoViewerWebView.setTranslationY(currentTranslationY); } @@ -16461,9 +16261,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -16477,11 +16277,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -16491,13 +16291,13 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); @@ -16545,7 +16345,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -16579,7 +16379,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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())); } @@ -16619,8 +16419,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); @@ -16755,36 +16556,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); @@ -16797,21 +16572,37 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; } @@ -16830,6 +16621,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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)); @@ -16865,7 +16661,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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()) { @@ -16911,7 +16707,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -16929,10 +16725,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); @@ -16947,7 +16742,18 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); } @@ -16962,6 +16768,94 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat videoForwardDrawable.setBounds(aspectRatioFrameLayout.getLeft(), aspectRatioFrameLayout.getTop() - h + (int) (currentTranslationY / currentScale), aspectRatioFrameLayout.getRight(), aspectRatioFrameLayout.getBottom() + h + (int) (currentTranslationY / currentScale)); videoForwardDrawable.draw(canvas); } + + drawFancyShadows(canvas); + } + + private Path clipFancyShadows; + private Paint topFancyShadowPaint, bottomFancyShadowPaint; + private LinearGradient topFancyShadow, bottomFancyShadow; + private Matrix topFancyShadowMatrix, bottomFancyShadowMatrix; + + private void drawFancyShadows(Canvas canvas) { + if (!fancyShadows) { + return; + } + 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) { @@ -17100,6 +16994,100 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -17274,8 +17262,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -17339,7 +17327,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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 = Math.min(135, containerView.getMeasuredWidth() / 8); if (x < side) { if (leftImage.hasImageSet()) { @@ -17375,8 +17363,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; } @@ -17384,8 +17372,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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); @@ -17420,8 +17408,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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; @@ -17494,7 +17482,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -17533,10 +17521,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -17599,7 +17587,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -17640,9 +17628,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat @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 @@ -17652,7 +17640,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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) { @@ -17661,19 +17649,19 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); } } @@ -17762,14 +17750,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -17984,16 +17965,24 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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() { @@ -18011,10 +18000,16 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) ); } @@ -18040,12 +18035,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) @@ -18133,13 +18122,13 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; } @@ -18181,33 +18170,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } - 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; @@ -18285,7 +18247,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat @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); @@ -18432,9 +18394,583 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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 2e2555b0e..ce7568c82 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.content.Context; 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.BackupImageView; 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 class 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 class PinchToZoomHelper { 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 class PinchToZoomHelper { 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 class PinchToZoomHelper { 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 @@ public class PinchToZoomHelper { 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 class PinchToZoomHelper { } 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 class PinchToZoomHelper { pinchTranslationX = 0f; pinchTranslationY = 0f; child.getParent().requestDisallowInterceptTouchEvent(true); - startZoom(child, image, textureView, messageObject); + startZoom(child, image, textureView, messageObject, spoilerEffect2Index); } @@ -815,4 +843,15 @@ public class PinchToZoomHelper { 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 5d6002ee6..bc0a8f1aa 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PopupNotificationActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PopupNotificationActivity.java @@ -162,7 +162,7 @@ public class PopupNotificationActivity extends Activity implements NotificationC Theme.createDialogsResources(this); Theme.createChatResources(this, false); - AndroidUtilities.fillStatusBarHeight(this); + AndroidUtilities.fillStatusBarHeight(this, false); for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) { 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 e86f2eb71..6be7483f4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PremiumPreviewFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PremiumPreviewFragment.java @@ -668,7 +668,7 @@ public class PremiumPreviewFragment extends BaseFragment implements Notification }); } - 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 73a99a4da..a0ddfbd53 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java @@ -191,6 +191,7 @@ import org.telegram.ui.Components.InstantCameraView; import org.telegram.ui.Components.ItemOptions; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LinkSpanDrawable; +import org.telegram.ui.Components.MediaActivity; import org.telegram.ui.Components.Paint.PersistColorPalette; import org.telegram.ui.Components.Premium.GiftPremiumBottomSheet; import org.telegram.ui.Components.Premium.PremiumFeatureBottomSheet; @@ -443,6 +444,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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(); @@ -652,8 +654,10 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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; @@ -733,7 +737,9 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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) { @@ -761,6 +767,26 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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 { @@ -2105,6 +2131,13 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. openDiscussion(); } else if (id == gift_premium) { showDialog(new GiftPremiumBottomSheet(ProfileActivity.this, getMessagesController().getUser(userId))); + } 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)); @@ -3416,6 +3449,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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) -> { @@ -3441,7 +3475,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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; @@ -3640,6 +3674,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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); @@ -3963,6 +3999,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. } openAvatar(); }); + avatarImage.setHasStories(needInsetForStories()); avatarImage.setOnLongClickListener(v -> { if (avatarBig != null || isTopic) { return false; @@ -4147,11 +4184,12 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. }; 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 { @@ -6280,6 +6318,9 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. } chatInfo = chatFull; } + if (sharedMediaLayout != null) { + sharedMediaLayout.setChatInfo(chatInfo); + } if (chatInfo != null && (chatInfo.call == null && !hasVoiceChatItem || chatInfo.call != null && hasVoiceChatItem)) { createActionBarMenu(false); } @@ -6657,6 +6698,10 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. avatarAnimationProgress = currentExpandAnimatorValue = progress; checkPhotoDescriptionAlpha(); + if (playProfileAnimation == 2) { + avatarImage.setProgressToExpand(progress); + } + listView.setAlpha(progress); listView.setTranslationX(AndroidUtilities.dp(48) - AndroidUtilities.dp(48) * progress); @@ -6804,8 +6849,10 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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)); @@ -6867,7 +6914,9 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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); @@ -6930,6 +6979,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. callback.run(); return; } + avatarImage.setProgressToExpand(0); listView.setLayerType(View.LAYER_TYPE_NONE, null); if (animatingItem != null) { ActionBarMenu menu = actionBar.createMenu(); @@ -7024,6 +7074,10 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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 a2c1ef0c8..2a0b42942 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/QrActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/QrActivity.java @@ -130,7 +130,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; @@ -386,7 +386,7 @@ public class QrActivity extends BaseFragment { 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); @@ -1464,10 +1464,11 @@ public class QrActivity extends BaseFragment { } 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 bc7b0be41..c4efb321f 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.app.Activity; 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; @@ -29,47 +36,97 @@ 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 com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import org.telegram.messenger.AndroidUtilities; +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.FileLoader; import org.telegram.messenger.FileLog; 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; public class SecretMediaViewer implements NotificationCenter.NotificationCenterDelegate, GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener { @@ -117,18 +174,23 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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); @@ -137,37 +199,55 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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) { @@ -187,14 +267,34 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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); + if (once) { + canvas.save(); + canvas.translate(deleteProgressRect.centerX() - onceLayoutWidth / 2f, deleteProgressRect.centerY() - onceLayoutHeight / 2f); + onceLayout.draw(canvas); + canvas.restore(); - timerParticles.draw(canvas, particlePaint, deleteProgressRect, radProgress, 1.0f); + 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(); } } @@ -230,6 +330,17 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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; @@ -237,8 +348,10 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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; @@ -254,6 +367,20 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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; @@ -417,7 +544,7 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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); @@ -426,9 +553,28 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD } 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 @@ -436,6 +582,8 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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); @@ -461,7 +609,7 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD if (playbackState == ExoPlayer.STATE_ENDED) { videoWatchedOneTime = true; if (closeVideoAfterWatch) { - closePhoto(true, true); + closePhoto(true, !ignoreDelete); } else { videoPlayer.seekTo(0); videoPlayer.play(); @@ -513,8 +661,60 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD } 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; @@ -592,23 +792,12 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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 @@ -618,8 +807,41 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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(); @@ -641,20 +863,27 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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 @@ -665,9 +894,87 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD } }); + 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; @@ -685,12 +992,410 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD windowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_SECURE; 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; } @@ -701,6 +1406,9 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD //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; @@ -735,8 +1443,15 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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(); @@ -784,8 +1499,6 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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) { @@ -793,7 +1506,17 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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; @@ -819,22 +1542,49 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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) { @@ -852,26 +1602,35 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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; @@ -882,6 +1641,10 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD containerView.invalidate(); if (closeAfterAnimation) { closePhoto(true, true); + } else { + if (ignoreDelete && MessagesController.getGlobalMainSettings().getInt("viewoncehint", 0) < 3) { + showSecretHint(); + } } }; imageMoveAnimation.setDuration(250); @@ -889,13 +1652,14 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD @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()); @@ -911,16 +1675,29 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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) { @@ -930,6 +1707,7 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD if (currentActionBarAnimation != null && currentActionBarAnimation.equals(animation)) { actionBar.setVisibility(View.GONE); currentActionBarAnimation = null; + captionScrollView.scrollTo(0, 0); } } }); @@ -939,8 +1717,12 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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); } } } @@ -949,6 +1731,10 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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); @@ -1112,6 +1898,10 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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(); @@ -1176,8 +1966,9 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD if (photoAnimationInProgress != 0) { if (Math.abs(photoTransitionAnimationStartTime - System.currentTimeMillis()) >= 500) { if (photoAnimationEndRunnable != null) { - photoAnimationEndRunnable.run(); + Runnable r = photoAnimationEndRunnable; photoAnimationEndRunnable = null; + r.run(); } photoAnimationInProgress = 0; } @@ -1202,10 +1993,19 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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); @@ -1235,7 +2035,7 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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(); @@ -1268,6 +2068,7 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD animateToY = translationY >= 0 ? h : -h; } animateToRadius = false; + showPlayButton(false, true); if (isVideo) { videoCrossfadeStarted = false; textureUploaded = false; @@ -1275,7 +2076,11 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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 { @@ -1284,7 +2089,11 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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) ); } @@ -1310,8 +2119,9 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD isVisible = false; AndroidUtilities.runOnUIThread(() -> { if (photoAnimationEndRunnable != null) { - photoAnimationEndRunnable.run(); + Runnable r = photoAnimationEndRunnable; photoAnimationEndRunnable = null; + r.run(); } }); } @@ -1322,12 +2132,17 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD } 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 = () -> { @@ -1348,8 +2163,9 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD @Override public void onAnimationEnd(Animator animation) { if (photoAnimationEndRunnable != null) { - photoAnimationEndRunnable.run(); + Runnable r = photoAnimationEndRunnable; photoAnimationEndRunnable = null; + r.run(); } } }); @@ -1359,6 +2175,10 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD } animatorSet.start(); } + if (onClose != null) { + onClose.run(); + onClose = null; + } return true; } @@ -1466,10 +2286,10 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD } 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(); @@ -1483,7 +2303,7 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD } 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; @@ -1677,7 +2497,21 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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; } @@ -1719,4 +2553,200 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD 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 8feb636ad..d45995789 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 class DarkThemeResourceProvider implements Theme.ResourcesProvider { 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 class DarkThemeResourceProvider implements Theme.ResourcesProvider { 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 1e6117745..08aa749bd 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 class DialogStoriesCell extends FrameLayout implements NotificationCenter 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 7d508e0e4..3c5952a2b 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 9af1f61c9..4b0130062 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 class PeerStoriesView extends SizeNotifierFrameLayout implements Notifica } 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 @@ public class PeerStoriesView extends SizeNotifierFrameLayout implements Notifica //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 class PeerStoriesView extends SizeNotifierFrameLayout implements Notifica 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 43b959a42..0386bb67c 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.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; -import android.graphics.Region; import android.graphics.drawable.Drawable; -import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.View; @@ -37,7 +35,6 @@ import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; -import org.telegram.messenger.Utilities; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.AnimatedFloat; @@ -428,10 +425,11 @@ public class ProfileStoriesView extends View implements NotificationCenter.Notif @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 e8e3e2543..c4cd57269 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 androidx.core.graphics.ColorUtils; 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 @@ public abstract class SelfStoriesPreviewView extends View { 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 abstract class SelfStoriesPreviewView extends View { 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 abstract class SelfStoriesPreviewView extends View { } void onBind(int position) { - SelfStoryViewsView.StoryItemInternal storyItem = storyItems.get(position); + storyItem = storyItems.get(position); if (isAttachedToWindow) { receiver.onAttachedToWindow(); } @@ -425,6 +426,10 @@ public abstract class SelfStoriesPreviewView extends View { } 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 abstract class SelfStoriesPreviewView extends View { canvas.restore(); } } + + public void update() { + updateLayout(); + } } private void formatCounterText(SpannableStringBuilder spannableStringBuilder, TLRPC.StoryViews storyViews, boolean twoLines) { @@ -499,5 +508,11 @@ public abstract class SelfStoriesPreviewView extends View { } 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 567625ec5..994d48a14 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 @@ package org.telegram.ui.Stories; 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.RectF; 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 android.widget.TextView; 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.ActionBarMenuSubItem; 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.BulletinFactory; 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.LaunchActivity; 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 class SelfStoryViewsPage extends FrameLayout implements NotificationCente } 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 class SelfStoryViewsPage extends FrameLayout implements NotificationCente 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 class SelfStoryViewsPage extends FrameLayout implements NotificationCente 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 cf51f151d..165e1e1c4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesController.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesController.java @@ -1933,7 +1933,7 @@ public class StoriesController { public void destroyStoryList(StoriesList list) { if (storiesLists[list.type] != null) { - storiesLists[list.type].remove(list.userId); + storiesLists[list.type].remove(list.dialogId); } } @@ -1961,7 +1961,7 @@ public class StoriesController { 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<>(); @@ -2050,7 +2050,7 @@ public class StoriesController { 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); @@ -2071,16 +2071,12 @@ public class StoriesController { 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) { @@ -2110,7 +2106,7 @@ public class StoriesController { } 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) { @@ -2213,11 +2209,7 @@ public class StoriesController { 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); } @@ -2241,16 +2233,11 @@ public class StoriesController { 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); @@ -2263,14 +2250,10 @@ public class StoriesController { 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(); } @@ -2293,7 +2276,7 @@ public class StoriesController { 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; @@ -2303,7 +2286,7 @@ public class StoriesController { private void resetCanLoad() { if (lastLoadTime != null) { - lastLoadTime.remove(Objects.hash(currentAccount, type, userId)); + lastLoadTime.remove(Objects.hash(currentAccount, type, dialogId)); } } @@ -2320,7 +2303,7 @@ public class StoriesController { 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 { @@ -2338,7 +2321,7 @@ public class StoriesController { 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) -> { @@ -2350,7 +2333,7 @@ public class StoriesController { 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; @@ -2387,7 +2370,7 @@ public class StoriesController { 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(); } @@ -2419,7 +2402,7 @@ public class StoriesController { // } 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; } @@ -2447,7 +2430,7 @@ public class StoriesController { } 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; } @@ -2512,7 +2495,7 @@ public class StoriesController { } 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 ff3012f0a..110a54783 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 class StoriesStorage { 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 class StoriesStorage { 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 class StoriesStorage { 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 class StoriesStorage { 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 class StoriesStorage { 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 @@ public class StoriesStorage { 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 @@ public class StoriesStorage { 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 @@ public class StoriesStorage { 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 @@ public class StoriesStorage { 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 class StoriesStorage { 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 a61474522..b742d6c4b 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.AnimatedFloat; 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 class StoryMediaAreasView extends FrameLayout implements View.OnClickList 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 000000000..de23f2e8d --- /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 1fe1732b8..52c7a32c8 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 class StoryViewer implements NotificationCenter.NotificationCenterDelegat 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 class StoryViewer implements NotificationCenter.NotificationCenterDelegat 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 class StoryViewer implements NotificationCenter.NotificationCenterDelegat close(false); } else { storiesViewPager.onNextIdle(() -> { - storiesViewPager.setDays(storiesList.userId, newDays, currentAccount); + storiesViewPager.setDays(storiesList.dialogId, newDays, currentAccount); }); } } else { @@ -1479,7 +1479,7 @@ public class StoryViewer implements NotificationCenter.NotificationCenterDelegat 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 class StoryViewer implements NotificationCenter.NotificationCenterDelegat 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 8dbac0efa..7f00900d0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/UserListPoller.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/UserListPoller.java @@ -6,6 +6,7 @@ import com.google.android.exoplayer2.util.Log; import org.checkerframework.checker.units.qual.A; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ChatObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.MessagesStorage; import org.telegram.messenger.NotificationCenter; @@ -15,6 +16,7 @@ import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; import org.telegram.ui.Cells.DialogCell; import org.telegram.ui.Cells.UserCell; +import org.telegram.ui.Components.Paint.Path; import org.telegram.ui.Components.RecyclerListView; import java.util.ArrayList; @@ -56,6 +58,7 @@ public class UserListPoller { 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) { @@ -98,6 +101,15 @@ public class UserListPoller { 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 9b9c4ff9e..edcf47e0a 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 class ButtonWithCounterView extends FrameLayout { 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 @@ public class ButtonWithCounterView extends FrameLayout { 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 @@ public class ButtonWithCounterView extends FrameLayout { 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 15049911b..d11305670 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.BitmapShader; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; -import android.graphics.CornerPathEffect; import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Paint; -import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.drawable.Drawable; -import android.os.Build; import android.text.Editable; import android.text.Layout; -import android.text.SpannableString; import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextWatcher; -import android.text.style.DynamicDrawableSpan; -import android.text.style.ImageSpan; import android.util.Log; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.ViewParent; -import android.view.ViewPropertyAnimator; -import android.view.ViewTreeObserver; -import android.view.WindowInsets; -import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.core.graphics.ColorUtils; import androidx.interpolator.view.animation.FastOutSlowInInterpolator; import org.telegram.messenger.AndroidUtilities; @@ -64,72 +49,94 @@ import org.telegram.messenger.LiteMode; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; -import org.telegram.messenger.SendMessagesHelper; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; -import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.AdjustPanLayoutHelper; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.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; - public CaptionContainerView(Context context, int currentAccount, StoryRecorder.WindowView rootView, FrameLayout containerView, Theme.ResourcesProvider resourcesProvider) { + 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; + + 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 @@ public class CaptionContainerView extends FrameLayout { 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) { + 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 void drawEmojiBackground(Canvas canvas, View view) { - AndroidUtilities.rectTmp.set(0, 0, view.getWidth(), view.getHeight()); - drawBackground(canvas, AndroidUtilities.rectTmp, 0, .95f, view); + 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 class CaptionContainerView extends FrameLayout { 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 class CaptionContainerView extends FrameLayout { @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 class CaptionContainerView extends FrameLayout { 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; - } + private final Runnable textChangeRunnable = () -> onTextChange(); + protected void onTextChange() {} - Utilities.Callback onPeriodSelected = period -> { - setPeriod(period); - if (onPeriodUpdate != null) { - onPeriodUpdate.run(period); - } - }; + public void invalidateBlur() { + invalidate(); + editText.getEditText().invalidate(); + editText.getEmojiButton().invalidate(); + if (mentionContainer != null) { + mentionContainer.invalidate(); + } + if (editText.getEmojiView() != null && customBlur()) { + editText.getEmojiView().invalidate(); + } + } - final boolean isPremium = UserConfig.getInstance(currentAccount).isPremium(); - - Utilities.Callback showPremiumHint = isPremium ? null : period -> { - if (onPremiumHintShow != null) { - onPremiumHintShow.run(period); - } - }; - - periodPopup = ItemOptions.makeOptions(rootView, resourcesProvider, periodButton); - for (int i = 0; i < periods.length; ++i) { - final int period = periods[i]; - periodPopup.add( - 0, - period == Integer.MAX_VALUE ? - LocaleController.getString("StoryPeriodKeep") : - LocaleController.formatPluralString("Hours", period / 3600), - periodIndex == i ? Theme.key_dialogTextBlue2 : Theme.key_actionBarDefaultSubmenuItem, - () -> onPeriodSelected.run(period) - ).putPremiumLock( - isPremium || period == 86400 || period == Integer.MAX_VALUE ? null : () -> showPremiumHint.run(period) - ); - } - periodPopup.addGap(); - 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 class CaptionContainerView extends FrameLayout { 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 class CaptionContainerView extends FrameLayout { 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 @@ public class CaptionContainerView extends FrameLayout { } } - 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 class CaptionContainerView extends FrameLayout { this.onHeightUpdate = onHeightUpdate; } - private Utilities.Callback onPeriodUpdate; - public void setOnPeriodUpdate(Utilities.Callback listener) { - this.onPeriodUpdate = listener; - } - - private Utilities.Callback onPremiumHintShow; - public void setOnPremiumHint(Utilities.Callback listener) { - this.onPremiumHintShow = listener; - } - - public void heightUpdate() { - if (onHeightUpdate != null) { - int height = editText.getHeight(); - if (keyboardShown) { - height = Math.max(dp(46), height); - } else { - height = Math.min(dp(150), height); - } - onHeightUpdate.run(height); - } - } - public int getEditTextHeight() { - int height = editText.getHeight(); - if (keyboardShown) { - height = Math.max(dp(46), height); - } else { - height = Math.min(dp(150), height); - } - return height; + return (int) heightAnimated.get(); + } + + public int getEditTextHeightClosedKeyboard() { + return Math.min(dp(82), editText.getHeight()); } private Utilities.Callback onKeyboardOpen; @@ -437,12 +481,20 @@ public class CaptionContainerView extends FrameLayout { 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(); } - keyboardHeight = Math.max(0, keyboardHeight - rootView.getBottomPadding(true)); + 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 - (sizeNotifierFrameLayout == null ? 0 : sizeNotifierFrameLayout.getBottomPadding())); View parent = (View) getParent(); parent.clearAnimation(); @@ -476,6 +528,14 @@ public class CaptionContainerView extends FrameLayout { 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 @@ public class CaptionContainerView extends FrameLayout { if (onKeyboardOpen != null) { onKeyboardOpen.run(show); } + beforeUpdateShownKeyboard(show); if (animated) { if (show) { if (mentionContainer != null) { @@ -499,24 +560,27 @@ public class CaptionContainerView extends FrameLayout { 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 class CaptionContainerView extends FrameLayout { 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 class CaptionContainerView extends FrameLayout { 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 class CaptionContainerView extends FrameLayout { 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 class CaptionContainerView extends FrameLayout { } } + 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 @@ public class CaptionContainerView extends FrameLayout { 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 class CaptionContainerView extends FrameLayout { 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 @@ public class CaptionContainerView extends FrameLayout { 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 @@ public class CaptionContainerView extends FrameLayout { ); 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 @@ public class CaptionContainerView extends FrameLayout { @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 class CaptionContainerView extends FrameLayout { } } + 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 000000000..98ba6fbda --- /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 765a54615..15fb236dc 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 @@ public class DownloadButton extends ImageView { 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 @@ public class DownloadButton extends ImageView { 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 @@ public class DownloadButton extends ImageView { 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 9a58c1a72..15b10cb9b 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 class DraftsController { 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 class DraftsController { 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 class DraftsController { 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 class DraftsController { 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 class DraftsController { } 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 991b78695..848269d9e 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 class DualCameraView extends CameraView implements CameraController.Error 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 0ea721d7a..b21f8f0bc 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.SparseArray; 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 android.widget.TextView; 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.ConnectionsManager; 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.AnimatedEmojiDrawable; 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.EmojiTabsStrip; 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.HashMap; 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 class EmojiBottomSheet extends BottomSheet implements NotificationCenter. 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 class EmojiBottomSheet extends BottomSheet implements NotificationCenter. 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 class EmojiBottomSheet extends BottomSheet implements NotificationCenter. 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 @@ public class EmojiBottomSheet extends BottomSheet implements NotificationCenter. 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 @@ public class EmojiBottomSheet extends BottomSheet implements NotificationCenter. 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 @@ public class EmojiBottomSheet extends BottomSheet implements NotificationCenter. } } 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 @@ public class EmojiBottomSheet extends BottomSheet implements NotificationCenter. }, 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 class EmojiBottomSheet extends BottomSheet implements NotificationCenter. 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 class EmojiBottomSheet extends BottomSheet implements NotificationCenter. } 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 class EmojiBottomSheet extends BottomSheet implements NotificationCenter. } } + 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 class EmojiBottomSheet extends BottomSheet implements NotificationCenter. 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 class EmojiBottomSheet extends BottomSheet implements NotificationCenter. 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 class EmojiBottomSheet extends BottomSheet implements NotificationCenter. 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 class EmojiBottomSheet extends BottomSheet implements NotificationCenter. } } - 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 @@ public class EmojiBottomSheet extends BottomSheet implements NotificationCenter. 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 class EmojiBottomSheet extends BottomSheet implements NotificationCenter. } 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 @@ public class EmojiBottomSheet extends BottomSheet implements NotificationCenter. } } - private static class StoryLocationComponentCell extends View { + public static final int WIDGET_LOCATION = 0; + public static final int WIDGET_PHOTO = 2; + + private class StoryWidgetsCell extends View { - private final TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); private final Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - - private final Drawable pin; - private StaticLayout layout; - private float layoutLeft, layoutWidth; - - private final RectF bounds = new RectF(); - private final ButtonBounce bounce = new ButtonBounce(this); - - public StoryLocationComponentCell(Context context) { - super(context); - - textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rcondensedbold.ttf")); - textPaint.setTextSize(dp(21.3f)); - textPaint.setColor(Color.WHITE); - - pin = context.getResources().getDrawable(R.drawable.map_pin3).mutate(); - pin.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); - + 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 int lastWidth; + private final List widgets = new ArrayList<>(); + + public StoryWidgetsCell(Context context) { + super(context); + setPadding(dp(0), 0, dp(0), 0); + + 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))); + } + + 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); + + abstract void draw(Canvas canvas, float left, float top); + + public void onAttachToWindow(boolean attached) { + + } + + } + + 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.top + height / 2 - dp(24) / 2), + (int) (bounds.left + dp(6 + 24)), + (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) { - super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(dp(60), MeasureSpec.EXACTLY)); + int y = 1; + float x = 0; - 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(); + final int width = MeasureSpec.getSize(widthMeasureSpec); + final int availableWidth = (int) ((width - getPaddingLeft() - getPaddingRight()) * 0.8f); - 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( - (int) (bounds.left + dp(6)), - (int) (bounds.centerY() - dp(12)), - (int) (bounds.left + dp(6 + 24)), - (int) (bounds.centerY() + dp(12)) - ); + 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 000000000..767a07a93 --- /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 4ac5d3fe0..3b7e39262 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.os.Build; 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.ButtonBounce; 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 class HintView2 extends View { 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 class HintView2 extends View { 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 class HintView2 extends View { 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 @@ public class HintView2 extends View { right = Math.max(right, textLayout.getLineRight(i)); } textLayoutWidth = Math.max(0, right - left); + textLayoutHeight = textLayout.getHeight(); textLayoutLeft = left; } @@ -529,7 +607,7 @@ public class HintView2 extends View { } 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 @@ public class HintView2 extends View { 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 @@ public class HintView2 extends View { } 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 @@ public class HintView2 extends View { 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 @@ public class HintView2 extends View { 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 @@ public class HintView2 extends View { 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 @@ public class HintView2 extends View { @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 906654cb9..02a0ba6bf 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.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.app.Activity; -import android.content.ClipData; -import android.content.ClipDescription; -import android.content.ClipboardManager; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -25,30 +21,26 @@ import android.graphics.Path; 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.Frame; import com.google.android.gms.vision.face.Face; import com.google.android.gms.vision.face.FaceDetector; -import org.checkerframework.checker.units.qual.A; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.Bitmaps; @@ -76,13 +67,11 @@ import org.telegram.messenger.DispatchQueue; import org.telegram.messenger.Emoji; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; -import org.telegram.messenger.ImageLocation; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; -import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; import org.telegram.messenger.VideoEditedInfo; @@ -92,12 +81,13 @@ import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarPopupWindow; import org.telegram.ui.ActionBar.AdjustPanLayoutHelper; import org.telegram.ui.ActionBar.AlertDialog; -import org.telegram.ui.ActionBar.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.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.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.ArrayList; import java.util.Arrays; import java.util.List; -public class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPaintView, PaintToolsView.Delegate, EntityView.EntityViewDelegate, PaintTextOptionsView.Delegate, SizeNotifierFrameLayoutPhoto.SizeNotifierFrameLayoutPhotoDelegate, StoryRecorder.Touchable { +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 class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai 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 class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai queue = new DispatchQueue("Paint"); bitmapToEdit = bitmap; + blurBitmapToEdit = blurBitmap; facesBitmap = originalBitmap; originalBitmapRotation = originalRotation; undoStore = new UndoStore(); @@ -373,7 +368,7 @@ public class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai 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 @@ public class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai 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 class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai } 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 @@ public class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai 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 @@ public class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai 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 class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai } }; 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 class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai } else { locationAlert.setStoryLocationPicker(); } + locationAlert.setOnDismissListener(di -> { + onOpenCloseStickersAlert(false); + }); locationAlert.init(); locationAlert.show(); } @@ -1767,10 +1769,6 @@ public class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai } else { hideEmojiView(); } - -// if (emojiView != null) { -// measureChild(emojiView, widthMeasureSpec, heightMeasureSpec); -// } } @Override @@ -2002,15 +2000,15 @@ public class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai @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 class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai } 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 class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai 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 @@ public class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai return button; } + public void setBlurManager(BlurringShader.BlurManager blurManager) { + this.blurManager = blurManager; + } + public class PopupButton extends LinearLayout { public TextView textView; @@ -3037,17 +3049,7 @@ public class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai } 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 @@ public class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai } 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 @@ public class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai }, 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 @@ public class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai 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)); @@ -3778,7 +3784,7 @@ public class PaintView extends SizeNotifierFrameLayoutPhoto implements IPhotoPai @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 ad3722c78..a38fe938b 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 class PreviewButtons extends FrameLayout { 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 3b3ef3adb..53727c7ee 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.graphics.SurfaceTexture; 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.view.ViewConfiguration; 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) { + private final BlurringShader.BlurManager blurManager; + + public PreviewView(Context context, BlurringShader.BlurManager blurManager) { super(context); - videoTimelineView = new VideoTimelinePlayView(context); - videoTimelineView.setMode(VideoTimelinePlayView.MODE_VIDEO); - videoTimelineView.setDelegate(new VideoTimelinePlayView.VideoTimelineViewDelegate() { - - private float durationOf(long ms) { - return (float) ms / (getDuration() == C.TIME_UNSET ? entry.duration : getDuration()); - } - - private long duration() { - return getDuration() == C.TIME_UNSET ? entry.duration : getDuration(); - } - - @Override - public void onLeftProgressChanged(float progress) { - if (videoPlayer != null) { - if (videoPlayer.isPlaying()) { - videoPlayer.pause(); - } - entry.left = progress; - entry.right = Utilities.clamp(Math.min(entry.right, entry.left + durationOf(MAX_DURATION)), 1, 0); - entry.left = Utilities.clamp(Math.min(entry.left, entry.right - durationOf(MIN_DURATION)), 1, 0); - videoTimelineView.setLeftRightProgress(entry.left, entry.right); - seekTo(entry.left); - videoTimelineView.setProgress(entry.left); - drag((long) (entry.left * duration())); - } - } - - @Override - public void onRightProgressChanged(float progress) { - if (videoPlayer != null) { - if (videoPlayer.isPlaying()) { - videoPlayer.pause(); - } - entry.right = progress; - entry.left = Utilities.clamp(Math.max(entry.left, entry.right - durationOf(MAX_DURATION)), 1, 0); - entry.right = Utilities.clamp(Math.max(entry.right, entry.left + durationOf(MIN_DURATION)), 1, 0); - videoTimelineView.setLeftRightProgress(entry.left, entry.right); - seekTo(entry.right); - videoTimelineView.setProgress(entry.right); - drag((long) (entry.right * duration())); - } - } - - @Override - public void onPlayProgressChanged(float progress) { - if (videoPlayer != null) { - seekTo(progress); - } - drag((long) (progress * duration())); - } - - private Runnable dragStart; - private boolean dragging; - private long lastDragTime; - - private void drag(long t) { - lastDragTime = t; - if (dragging) { - onTimeDrag(false, lastDragTime, false); - } else if (dragStart == null) { - AndroidUtilities.runOnUIThread(dragStart = () -> { - dragging = true; - dragStart = null; - onTimeDrag(true, lastDragTime, false); - }, 150); - } - } - - @Override - public void didStartDragging(int type) { - if (videoPlayer == null) { - return; - } - updatePauseReason(-1, true); - drag((long) (videoTimelineView.getProgressOf(type) * duration())); - } - - @Override - public void didStopDragging(int type) { - if (seekToRunnable != null) { - AndroidUtilities.cancelRunOnUIThread(seekToRunnable); - seekToRunnable.run(); - } - if (videoPlayer != null && !videoPlayer.isPlaying()) { - float currentProgress = videoPlayer.getCurrentPosition() / (float) getDuration(); - if (currentProgress < entry.left || currentProgress > entry.right) { - seekTo(entry.left * getDuration()); - } - updatePauseReason(-1, false); - } - dragging = false; - if (dragStart != null) { - AndroidUtilities.cancelRunOnUIThread(dragStart); - dragStart = null; - } - onTimeDrag(false, lastDragTime, true); - } - - private Runnable seekToRunnable; - private int seekTo; - private boolean wasPlaying; - - private void seekTo(float progress) { - if (videoPlayer == null) { - return; - } - seekTo = (int) (getDuration() * progress); - if (SharedConfig.getDevicePerformanceClass() == SharedConfig.PERFORMANCE_CLASS_HIGH) { - if (videoPlayer != null) { - videoPlayer.seekTo(seekTo); - } - applyMatrix(); - seekToRunnable = null; - } else if (seekToRunnable == null) { - AndroidUtilities.runOnUIThread(seekToRunnable = () -> { - if (videoPlayer != null) { - videoPlayer.seekTo(seekTo); - } - applyMatrix(); - seekToRunnable = null; - }, 100); - } - } - }); + this.blurManager = blurManager; snapPaint.setStrokeWidth(AndroidUtilities.dp(1)); snapPaint.setStyle(Paint.Style.STROKE); @@ -213,16 +91,12 @@ public class PreviewView extends FrameLayout { 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 class PreviewView extends FrameLayout { setupImage(null); setupParts(null); gradientPaint.setShader(null); + setupAudio((StoryEntry) null, false); return; } if (entry.isVideo) { @@ -251,17 +126,203 @@ public class PreviewView extends FrameLayout { } 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 @@ public class PreviewView extends FrameLayout { 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 @@ public class PreviewView extends FrameLayout { 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 @@ public class PreviewView extends FrameLayout { 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 @@ public class PreviewView extends FrameLayout { } }).start(); } + if (timelineView != null) { + timelineView.setVideo(null, 1); + } AndroidUtilities.cancelRunOnUIThread(updateProgressRunnable); if (whenReady != null) { AndroidUtilities.runOnUIThread(whenReady); @@ -434,6 +480,7 @@ public class PreviewView extends FrameLayout { 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 class PreviewView extends FrameLayout { } 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 class PreviewView extends FrameLayout { 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 class PreviewView extends FrameLayout { } } - 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 class PreviewView extends FrameLayout { 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 class PreviewView extends FrameLayout { 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 e1b163f9e..0be5cebb5 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.FileRefController; 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 class StoryEntry extends IStoryPart { // paint public File paintFile; + public File paintBlurFile; public File paintEntitiesFile; public long averageDuration = 5000; public ArrayList mediaEntities; @@ -168,6 +180,9 @@ public class StoryEntry extends IStoryPart { 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 @@ public class StoryEntry extends IStoryPart { ); } - - 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 class StoryEntry extends IStoryPart { 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), 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), 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), 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); + } } } - 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); - } - } + return finalBitmap; + } - 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); - } + public void buildPhoto(File dest) { + final Bitmap finalBitmap = buildBitmap(1f, null); + if (thumbBitmap != null) { + thumbBitmap.recycle(); + thumbBitmap = null; } - - 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); - } - } - 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 class StoryEntry extends IStoryPart { } catch (Exception e) { FileLog.e(e); } - finalBitmap.recycle(); } @@ -533,6 +565,7 @@ public class StoryEntry extends IStoryPart { 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 class StoryEntry extends IStoryPart { 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 class StoryEntry extends IStoryPart { 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 class StoryEntry extends IStoryPart { 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 d2c5aaca5..73b276080 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 class StoryPrivacyBottomSheet extends BottomSheet implements Notification 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 c52d13bd3..d15837c4c 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.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; -import android.animation.StateListAnimator; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.app.Activity; @@ -18,7 +17,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; -import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; @@ -42,7 +40,6 @@ import android.hardware.Camera; import android.net.Uri; import android.os.Build; import android.os.Parcelable; -import android.provider.MediaStore; import android.text.Layout; import android.text.Spannable; import android.text.SpannableString; @@ -50,15 +47,11 @@ import android.text.SpannableStringBuilder; 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.ViewGroup; import android.view.ViewOutlineProvider; import android.view.WindowInsets; import android.view.WindowManager; -import android.view.inputmethod.InputMethodManager; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; @@ -77,19 +69,16 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.graphics.ColorUtils; -import androidx.core.view.WindowCompat; import androidx.core.view.WindowInsetsCompat; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringAnimation; -import androidx.exifinterface.media.ExifInterface; import androidx.interpolator.view.animation.FastOutSlowInInterpolator; -import org.checkerframework.checker.units.qual.A; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.BotWebViewVibrationEffect; -import org.telegram.messenger.BuildVars; +import org.telegram.messenger.DialogObject; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.ImageLoader; @@ -98,6 +87,7 @@ import org.telegram.messenger.LiteMode; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaController; import org.telegram.messenger.MediaDataController; +import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; @@ -105,7 +95,6 @@ import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; import org.telegram.messenger.UserObject; import org.telegram.messenger.Utilities; -import org.telegram.messenger.VideoEditedInfo; import org.telegram.messenger.camera.CameraController; import org.telegram.messenger.camera.CameraSession; import org.telegram.tgnet.TLObject; @@ -116,16 +105,17 @@ import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.SimpleTextView; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.AlertsCreator; +import org.telegram.ui.Components.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.LimitReachedBottomSheet; 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.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; public class StoryRecorder implements NotificationCenter.NotificationCenterDelegate { @@ -683,8 +669,13 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg } } - 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg } } - 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg private ImageView backButton; private SimpleTextView titleTextView; private StoryPrivacyBottomSheet privacySheet; + private BlurringShader.BlurManager blurManager; /* PAGE_CAMERA */ private ImageView cameraViewThumb; @@ -1355,10 +1367,10 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg /* 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg } } }); + 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 class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 class StoryRecorder implements NotificationCenter.NotificationCenterDeleg super.applyMatrix(); applyFilterMatrix(); } + @Override public void onEntityDraggedTop(boolean value) { previewHighlight.show(true, value, actionBarContainer); @@ -1580,10 +1592,14 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 class StoryRecorder implements NotificationCenter.NotificationCenterDeleg }); 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 class StoryRecorder implements NotificationCenter.NotificationCenterDeleg } }); - 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 class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 class StoryRecorder implements NotificationCenter.NotificationCenterDeleg videoTimerView.setRecording(true, true); showVideoTimer(true, true); - }, cameraView, false); + }, cameraView, true); if (!isVideo) { isVideo = true; @@ -2624,7 +2697,9 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg // 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg paintView.shutdown(); containerView.removeView(paintView); - if (paintViewBitmap != null) { - paintViewBitmap.recycle(); - paintViewBitmap = null; - } paintView = null; if (paintViewRenderView != null) { previewContainer.removeView(paintViewRenderView); @@ -3589,7 +3682,7 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg } 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg } 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg } } -// 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 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg previewContainer.removeView(photoFilterViewTextureView); photoFilterViewTextureView = null; } - previewView.setFilterTextureView(null); + previewView.setFilterTextureView(null, null); if (photoFilterViewBlurControl != null) { previewContainer.removeView(photoFilterViewBlurControl); photoFilterViewBlurControl = null; @@ -3889,10 +3929,6 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg photoFilterViewCurvesControl = null; } photoFilterView = null; - if (photoFilterBitmap != null) { - photoFilterBitmap.recycle(); - photoFilterBitmap = null; - } // photoFilterStartMatrix = null; // photoFilterEndMatrix = null; // if (photoFilterAnimator != null) { @@ -4349,7 +4385,7 @@ public class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 000000000..6269e2676 --- /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 000000000..ac60c2d4d --- /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 000000000..2981cb006 --- /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 b8c4bbb29..1a75e863e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/UsersSelectActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/UsersSelectActivity.java @@ -720,7 +720,7 @@ public class UsersSelectActivity extends BaseFragment implements NotificationCen 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 000000000..04ed92bc3 --- /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 000000000..32cdf35f3 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 000000000..db7bb482d 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 000000000..d1e6a8a77 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 000000000..e005cbcfd 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 000000000..4e4c85384 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 000000000..8ed6d0c77 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 000000000..40965a394 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 000000000..69b923bbe 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 000000000..24099817f 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 000000000..ee03ccda4 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 000000000..13a15cc99 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 000000000..6445f3ff1 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 000000000..ced980867 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 000000000..252d0d449 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 000000000..08ffd9656 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 000000000..d8e03f4fd 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 000000000..f12300eda 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 000000000..086ae690d 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 000000000..12a01e491 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 000000000..f3fcccccc 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 000000000..e67362229 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 000000000..882a2f96d 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 000000000..6f746da45 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 000000000..5e7bc5650 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 000000000..3cf213c73 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 000000000..7aafc1a48 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 000000000..02e6db33a 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 000000000..32b576207 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 000000000..da4c0838a 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 000000000..81b35e889 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 000000000..0694ba1a0 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 000000000..4e2f852bc 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 000000000..2049c4b15 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 000000000..a303f38d5 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 000000000..f7567f2e7 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 000000000..5e4b9788a 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 000000000..b358190fe 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 000000000..80bec6859 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 000000000..3e7c91d96 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 000000000..5535db908 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 000000000..304fd8095 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 000000000..0279dde61 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 000000000..460b7929f 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 000000000..f19456be5 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 000000000..29f506bc5 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 000000000..391295db6 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 000000000..0811211ce 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 000000000..1291a1130 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 000000000..2b3aa9ddf 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 000000000..2468172be 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 000000000..db77cff2d 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 000000000..fdd33532f 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 000000000..ae16a8fb5 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 000000000..46fcbfc5c 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 000000000..f962825e9 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 000000000..937d5ecea 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 000000000..3a4711b37 --- /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 000000000..c81559c9c --- /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 000000000..832e9f964 --- /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 000000000..3c87b82fe --- /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 3b99f0f1a..2effbd320 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 @@ -5747,7 +5751,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. @@ -6152,6 +6156,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. @@ -6714,6 +6722,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 @@ -6864,7 +6873,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 @@ -6875,6 +6886,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 @@ -6887,6 +6901,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 @@ -6965,12 +6981,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. @@ -7047,6 +7072,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.** @@ -7118,5 +7144,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 diff --git a/gradle.properties b/gradle.properties index 6a89df0f7..c1bc1ef7a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,8 +13,8 @@ # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true #Sat Mar 12 05:53:50 MSK 2016 -APP_VERSION_CODE=3804 -APP_VERSION_NAME=10.0.5 +APP_VERSION_CODE=3867 +APP_VERSION_NAME=10.0.8 APP_PACKAGE=org.telegram.messenger RELEASE_KEY_PASSWORD=android RELEASE_KEY_ALIAS=androidkey