diff --git a/TMessagesProj/build.gradle b/TMessagesProj/build.gradle index 9ee9a793b5f..5176e742a97 100644 --- a/TMessagesProj/build.gradle +++ b/TMessagesProj/build.gradle @@ -210,7 +210,7 @@ apply plugin: 'com.google.gms.google-services' task checkVisibility { doFirst { def isPrivateBuild = project.gradle.startParameter.taskNames.find { - it.contains("HA_private") || it.contains("Debug") || it.contains("Release") + it.contains("HA_private") || it.contains("HA_hardcore") || it.contains("Debug") || it.contains("Release") } def isPublicAllowed = !project.hasProperty("IS_PRIVATE") || !project.property("IS_PRIVATE").toBoolean() if (!isPrivateBuild && !isPublicAllowed) { diff --git a/TMessagesProj/jni/CMakeLists.txt b/TMessagesProj/jni/CMakeLists.txt index f2b998fcf3a..df9bd34ac35 100644 --- a/TMessagesProj/jni/CMakeLists.txt +++ b/TMessagesProj/jni/CMakeLists.txt @@ -13,6 +13,7 @@ ${CMAKE_HOME_DIRECTORY}/ffmpeg/${ANDROID_ABI}/libavresample.a, ${CMAKE_HOME_DIRECTORY}/ffmpeg/${ANDROID_ABI}/libavutil.a, ${CMAKE_HOME_DIRECTORY}/ffmpeg/${ANDROID_ABI}/libswresample.a, ${CMAKE_HOME_DIRECTORY}/ffmpeg/${ANDROID_ABI}/libvpx.a, +${CMAKE_HOME_DIRECTORY}/ffmpeg/${ANDROID_ABI}/libdav1d.a, ${CMAKE_HOME_DIRECTORY}/boringssl/lib/libssl_${ANDROID_ABI}.a, ${CMAKE_HOME_DIRECTORY}/boringssl/lib/libcrypto_${ANDROID_ABI}.a") @@ -49,6 +50,8 @@ set_target_properties(ssl PROPERTIES IMPORTED_LOCATION ${CMAKE_HOME_DIRECTORY}/b add_library(libvpx STATIC IMPORTED) set_target_properties(libvpx PROPERTIES IMPORTED_LOCATION ${CMAKE_HOME_DIRECTORY}/ffmpeg/${ANDROID_ABI}/libvpx.a) +add_library(libdav1d STATIC IMPORTED) +set_target_properties(libdav1d PROPERTIES IMPORTED_LOCATION ${CMAKE_HOME_DIRECTORY}/ffmpeg/${ANDROID_ABI}/libdav1d.a) #tgnet #add_library(mozjpeg STATIC @@ -641,6 +644,7 @@ target_link_libraries(${NATIVE_LIB} avresample swresample libvpx + libdav1d avutil ssl crypto diff --git a/TMessagesProj/jni/TgNetWrapper.cpp b/TMessagesProj/jni/TgNetWrapper.cpp index cdb8e10b7e3..34cbe2e477a 100644 --- a/TMessagesProj/jni/TgNetWrapper.cpp +++ b/TMessagesProj/jni/TgNetWrapper.cpp @@ -85,6 +85,10 @@ jint getCurrentTime(JNIEnv *env, jclass c, jint instanceNum) { return ConnectionsManager::getInstance(instanceNum).getCurrentTime(); } +jint getCurrentPingTime(JNIEnv *env, jclass c, jint instanceNum) { + return ConnectionsManager::getInstance(instanceNum).getCurrentPingTime(); +} + jint getCurrentDatacenterId(JNIEnv *env, jclass c, jint instanceNum) { return ConnectionsManager::getInstance(instanceNum).getCurrentDatacenterId(); } @@ -486,6 +490,7 @@ static const char *ConnectionsManagerClassPathName = "org/telegram/tgnet/Connect static JNINativeMethod ConnectionsManagerMethods[] = { {"native_getCurrentTimeMillis", "(I)J", (void *) getCurrentTimeMillis}, {"native_getCurrentTime", "(I)I", (void *) getCurrentTime}, + {"native_getCurrentPingTime", "(I)I", (void *) getCurrentPingTime}, {"native_getCurrentDatacenterId", "(I)I", (void *) getCurrentDatacenterId}, {"native_isTestBackend", "(I)I", (void *) isTestBackend}, {"native_getTimeDifference", "(I)I", (void *) getTimeDifference}, diff --git a/TMessagesProj/jni/ffmpeg/arm64-v8a/libavcodec.a b/TMessagesProj/jni/ffmpeg/arm64-v8a/libavcodec.a index 3e86a329dbf..bcd7507aa56 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 4bd6439a4da..85977b7a35f 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 591f2207b53..2cd1956402e 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 6b3846183c3..cacce3eed9b 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/libdav1d.a b/TMessagesProj/jni/ffmpeg/arm64-v8a/libdav1d.a new file mode 100644 index 00000000000..0d5bca31856 Binary files /dev/null and b/TMessagesProj/jni/ffmpeg/arm64-v8a/libdav1d.a differ diff --git a/TMessagesProj/jni/ffmpeg/arm64-v8a/libswresample.a b/TMessagesProj/jni/ffmpeg/arm64-v8a/libswresample.a index 69efdf612d5..f876f392fbb 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 9f0c6d211c6..7390fef5daf 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 0d8886b8b62..0bb77cf7355 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 4d4a948567d..21bb8cd8049 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 c43fffeaace..1f29b8e7e7a 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 a4816891dde..43f5ae9eba3 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 5dde31d83b6..8a6a17e9d23 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/libdav1d.a b/TMessagesProj/jni/ffmpeg/armeabi-v7a/libdav1d.a new file mode 100644 index 00000000000..564a45c4012 Binary files /dev/null and b/TMessagesProj/jni/ffmpeg/armeabi-v7a/libdav1d.a differ diff --git a/TMessagesProj/jni/ffmpeg/armeabi-v7a/libswresample.a b/TMessagesProj/jni/ffmpeg/armeabi-v7a/libswresample.a index fb86241f981..655f39341de 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 f35501bcaee..e5f72f9a5a4 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 a6d9ffcaa7d..ab2abbee525 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 index dd912321ca0..636f2e0180d 100755 --- a/TMessagesProj/jni/ffmpeg/build_ffmpeg/build_ffmpeg.sh +++ b/TMessagesProj/jni/ffmpeg/build_ffmpeg/build_ffmpeg.sh @@ -5,6 +5,7 @@ # ffmpeg 4.4.3 # lib vpx 1.10.9 # NDK for compile libvpx. Last successful build with 21.1.6352462 +# and dav1d. Last successful build with # NDK r10e for compile ffmpeg # # 1) download ffmpeg @@ -12,8 +13,18 @@ # 3) download lib vpx # 4) copy libvpx to vpx-android folder and rename as libvpx # 5) copy build_ffmpeg foleder in ffmepg directory -# 6) run build_ffmpeg.sh -# 7) see compiled library in build_ffmpeg/adnroid folder +# 6) download dav1d into android-dav1d/dav1d folder +# 7.1) in ffmpeg fix typos in 3 files, replacing 'int B0' into 'int b0' +# 7.2) install python3.9 and replace python in vpx-android/_settings.sh +# 7.3) (macos) replace HOST_NUM_CORES with $(sysctl -n hw.physicalcpu) +# 7.4) (macos) press allow and open for each executable in system preferences +# 8) patch ffmpeg/configure to take dav1d as an external lib from folder: +# enabled libdav1d && { +# require_pkg_config libdav1d "libdav1d >= 0.5.0" "dav1d/dav1d.h" dav1d_version || +# check_lib libdav1d "dav1d/dav1d.h" "DAV1D_VERSION" "-ldav1d $libm_extralibs $pthreads_extralibs" +# } +# 9) run build_ffmpeg.sh +# 10) see compiled library in build_ffmpeg/android folder NDK="/opt/android/ndk/android-ndk-r21e" NDK_r10e="/opt/android/ndk/android-ndk-r10e" @@ -24,6 +35,11 @@ export ANDROID_NDK=$NDK sh build-vpx.sh cd .. +#build dav1d +cd ./dav1d-android +export ANDROID_NDK=$NDK +./build_dav1d.sh +cd .. NDK=$NDK_r10e @@ -37,7 +53,7 @@ echo "Configuring..." INCLUDES=" -I${PREFIX}/include" LIBS=" -L${PREFIX}/lib" - + ./configure \ --cc=$CC \ --nm=$NM \ @@ -80,6 +96,10 @@ LIBS=" -L${PREFIX}/lib" --enable-muxer=matroska \ --enable-bsf=vp9_superframe \ --enable-bsf=vp9_raw_reorder \ +\ +--enable-libdav1d \ +--enable-decoder=libdav1d \ +--enable-decoder=av1 \ --enable-runtime-cpudetect \ --enable-pthreads \ --enable-avresample \ @@ -106,7 +126,7 @@ $ADDITIONAL_CONFIGURE_FLAG #echo "continue?" #read -make -j8 install +make -j${HOST_NUM_CORES} install } diff --git a/TMessagesProj/jni/ffmpeg/build_ffmpeg/dav1d-android/build_dav1d.sh b/TMessagesProj/jni/ffmpeg/build_ffmpeg/dav1d-android/build_dav1d.sh new file mode 100755 index 00000000000..22b47492630 --- /dev/null +++ b/TMessagesProj/jni/ffmpeg/build_ffmpeg/dav1d-android/build_dav1d.sh @@ -0,0 +1,87 @@ +#!/bin/bash + +PREFIX="$(pwd)/../android" +mkdir -p "$PREFIX" +echo "Building dav1d into $PREFIX" + +pushd dav1d + +meson setup builddir-arm64 \ + --prefix "$PREFIX/arm64-v8a" \ + --libdir="lib" \ + --includedir="include" \ + --buildtype=release -Denable_tests=false -Denable_tools=false -Ddefault_library=static \ + --cross-file <(echo " + [binaries] + c = '${ANDROID_NDK}/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android21-clang' + ar = '${ANDROID_NDK}/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android-ar' + + [host_machine] + system = 'android' + cpu_family = 'aarch64' + cpu = 'arm64' + endian = 'little' + ") +ninja -C builddir-arm64 +ninja -C builddir-arm64 install + +meson setup builddir-armv7 \ + --prefix "$PREFIX/armeabi-v7a" \ + --libdir="lib" \ + --includedir="include" \ + --buildtype=release -Denable_tests=false -Denable_tools=false -Ddefault_library=static \ + --cross-file <(echo " + [binaries] + c = '${ANDROID_NDK}/toolchains/llvm/prebuilt/darwin-x86_64/bin/armv7a-linux-androideabi21-clang' + ar = '${ANDROID_NDK}/toolchains/llvm/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ar' + + [host_machine] + system = 'android' + cpu_family = 'arm' + cpu = 'armv7' + endian = 'little' + ") \ + -Dc_args="-DDAV1D_NO_GETAUXVAL" +ninja -C builddir-armv7 +ninja -C builddir-armv7 install + +meson setup builddir-x86 \ + --prefix "$PREFIX/x86" \ + --libdir="lib" \ + --includedir="include" \ + --buildtype=release -Denable_tests=false -Denable_tools=false -Ddefault_library=static \ + --cross-file <(echo " + [binaries] + c = '${ANDROID_NDK}/toolchains/llvm/prebuilt/darwin-x86_64/bin/i686-linux-android21-clang' + ar = '${ANDROID_NDK}/toolchains/llvm/prebuilt/darwin-x86_64/bin/i686-linux-android-ar' + + [host_machine] + system = 'android' + cpu_family = 'x86' + cpu = 'i686' + endian = 'little' + ") +ninja -C builddir-x86 +ninja -C builddir-x86 install + +meson setup builddir-x86_64 \ + --prefix "$PREFIX/x86_64" \ + --libdir="lib" \ + --includedir="include" \ + --buildtype=release -Denable_tests=false -Denable_tools=false -Ddefault_library=static \ + --cross-file <(echo " + [binaries] + c = '${ANDROID_NDK}/toolchains/llvm/prebuilt/darwin-x86_64/bin/x86_64-linux-android21-clang' + ar = '${ANDROID_NDK}/toolchains/llvm/prebuilt/darwin-x86_64/bin/x86_64-linux-android-ar' + + [host_machine] + system = 'android' + cpu_family = 'x86_64' + cpu = 'x86_64' + endian = 'little' + ") +ninja -C builddir-x86_64 +ninja -C builddir-x86_64 install + +popd + diff --git a/TMessagesProj/jni/ffmpeg/include/dav1d/common.h b/TMessagesProj/jni/ffmpeg/include/dav1d/common.h new file mode 100644 index 00000000000..290e6ace328 --- /dev/null +++ b/TMessagesProj/jni/ffmpeg/include/dav1d/common.h @@ -0,0 +1,94 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DAV1D_COMMON_H +#define DAV1D_COMMON_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef DAV1D_API + #if defined _WIN32 + #if defined DAV1D_BUILDING_DLL + #define DAV1D_API __declspec(dllexport) + #else + #define DAV1D_API + #endif + #else + #if __GNUC__ >= 4 + #define DAV1D_API __attribute__ ((visibility ("default"))) + #else + #define DAV1D_API + #endif + #endif +#endif + +#if EPERM > 0 +#define DAV1D_ERR(e) (-(e)) ///< Negate POSIX error code. +#else +#define DAV1D_ERR(e) (e) +#endif + +/** + * A reference-counted object wrapper for a user-configurable pointer. + */ +typedef struct Dav1dUserData { + const uint8_t *data; ///< data pointer + struct Dav1dRef *ref; ///< allocation origin +} Dav1dUserData; + +/** + * Input packet metadata which are copied from the input data used to + * decode each image into the matching structure of the output image + * returned back to the user. Since these are metadata fields, they + * can be used for other purposes than the documented ones, they will + * still be passed from input data to output picture without being + * used internally. + */ +typedef struct Dav1dDataProps { + int64_t timestamp; ///< container timestamp of input data, INT64_MIN if unknown (default) + int64_t duration; ///< container duration of input data, 0 if unknown (default) + int64_t offset; ///< stream offset of input data, -1 if unknown (default) + size_t size; ///< packet size, default Dav1dData.sz + struct Dav1dUserData user_data; ///< user-configurable data, default NULL members +} Dav1dDataProps; + +/** + * Release reference to a Dav1dDataProps. + */ +DAV1D_API void dav1d_data_props_unref(Dav1dDataProps *props); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* DAV1D_COMMON_H */ diff --git a/TMessagesProj/jni/ffmpeg/include/dav1d/data.h b/TMessagesProj/jni/ffmpeg/include/dav1d/data.h new file mode 100644 index 00000000000..e551ad65066 --- /dev/null +++ b/TMessagesProj/jni/ffmpeg/include/dav1d/data.h @@ -0,0 +1,117 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DAV1D_DATA_H +#define DAV1D_DATA_H + +#include +#include + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Dav1dData { + const uint8_t *data; ///< data pointer + size_t sz; ///< data size + struct Dav1dRef *ref; ///< allocation origin + Dav1dDataProps m; ///< user provided metadata passed to the output picture +} Dav1dData; + +/** + * Allocate data. + * + * @param data Input context. + * @param sz Size of the data that should be allocated. + * + * @return Pointer to the allocated buffer on success. NULL on error. + */ +DAV1D_API uint8_t * dav1d_data_create(Dav1dData *data, size_t sz); + +/** + * Wrap an existing data array. + * + * @param data Input context. + * @param buf The data to be wrapped. + * @param sz Size of the data. + * @param free_callback Function to be called when we release our last + * reference to this data. In this callback, $buf will be + * the $buf argument to this function, and $cookie will + * be the $cookie input argument to this function. + * @param cookie Opaque parameter passed to free_callback(). + * + * @return 0 on success. A negative DAV1D_ERR value on error. + */ +DAV1D_API int dav1d_data_wrap(Dav1dData *data, const uint8_t *buf, size_t sz, + void (*free_callback)(const uint8_t *buf, void *cookie), + void *cookie); + +/** + * Wrap a user-provided data pointer into a reference counted object. + * + * data->m.user_data field will initialized to wrap the provided $user_data + * pointer. + * + * $free_callback will be called on the same thread that released the last + * reference. If frame threading is used, make sure $free_callback is + * thread-safe. + * + * @param data Input context. + * @param user_data The user data to be wrapped. + * @param free_callback Function to be called when we release our last + * reference to this data. In this callback, $user_data + * will be the $user_data argument to this function, and + * $cookie will be the $cookie input argument to this + * function. + * @param cookie Opaque parameter passed to $free_callback. + * + * @return 0 on success. A negative DAV1D_ERR value on error. + */ +DAV1D_API int dav1d_data_wrap_user_data(Dav1dData *data, + const uint8_t *user_data, + void (*free_callback)(const uint8_t *user_data, + void *cookie), + void *cookie); + +/** + * Free the data reference. + * + * The reference count for data->m.user_data will be decremented (if it has been + * initialized with dav1d_data_wrap_user_data). The $data object will be memset + * to 0. + * + * @param data Input context. + */ +DAV1D_API void dav1d_data_unref(Dav1dData *data); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* DAV1D_DATA_H */ diff --git a/TMessagesProj/jni/ffmpeg/include/dav1d/dav1d.h b/TMessagesProj/jni/ffmpeg/include/dav1d/dav1d.h new file mode 100644 index 00000000000..e8f07057791 --- /dev/null +++ b/TMessagesProj/jni/ffmpeg/include/dav1d/dav1d.h @@ -0,0 +1,329 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DAV1D_H +#define DAV1D_H + +#include +#include + +#include "common.h" +#include "picture.h" +#include "data.h" +#include "version.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Dav1dContext Dav1dContext; +typedef struct Dav1dRef Dav1dRef; + +#define DAV1D_MAX_THREADS 256 +#define DAV1D_MAX_FRAME_DELAY 256 + +typedef struct Dav1dLogger { + void *cookie; ///< Custom data to pass to the callback. + /** + * Logger callback. May be NULL to disable logging. + * + * @param cookie Custom pointer passed to all calls. + * @param format The vprintf compatible format string. + * @param ap List of arguments referenced by the format string. + */ + void (*callback)(void *cookie, const char *format, va_list ap); +} Dav1dLogger; + +enum Dav1dInloopFilterType { + DAV1D_INLOOPFILTER_NONE = 0, + DAV1D_INLOOPFILTER_DEBLOCK = 1 << 0, + DAV1D_INLOOPFILTER_CDEF = 1 << 1, + DAV1D_INLOOPFILTER_RESTORATION = 1 << 2, + DAV1D_INLOOPFILTER_ALL = DAV1D_INLOOPFILTER_DEBLOCK | + DAV1D_INLOOPFILTER_CDEF | + DAV1D_INLOOPFILTER_RESTORATION, +}; + +enum Dav1dDecodeFrameType { + DAV1D_DECODEFRAMETYPE_ALL = 0, ///< decode and return all frames + DAV1D_DECODEFRAMETYPE_REFERENCE = 1,///< decode and return frames referenced by other frames only + DAV1D_DECODEFRAMETYPE_INTRA = 2, ///< decode and return intra frames only (includes keyframes) + DAV1D_DECODEFRAMETYPE_KEY = 3, ///< decode and return keyframes only +}; + +typedef struct Dav1dSettings { + int n_threads; ///< number of threads (0 = number of logical cores in host system, default 0) + int max_frame_delay; ///< Set to 1 for low-latency decoding (0 = ceil(sqrt(n_threads)), default 0) + int apply_grain; ///< whether to apply film grain on output frames (default 1) + int operating_point; ///< select an operating point for scalable AV1 bitstreams (0 - 31, default 0) + int all_layers; ///< output all spatial layers of a scalable AV1 biststream (default 1) + unsigned frame_size_limit; ///< maximum frame size, in pixels (0 = unlimited, default 0) + Dav1dPicAllocator allocator; ///< Picture allocator callback. + Dav1dLogger logger; ///< Logger callback. + int strict_std_compliance; ///< whether to abort decoding on standard compliance violations + ///< that don't affect actual bitstream decoding (e.g. inconsistent + ///< or invalid metadata, default 0) + int output_invisible_frames; ///< output invisibly coded frames (in coding order) in addition + ///< to all visible frames. Because of show-existing-frame, this + ///< means some frames may appear twice (once when coded, + ///< once when shown, default 0) + enum Dav1dInloopFilterType inloop_filters; ///< postfilters to enable during decoding (default + ///< DAV1D_INLOOPFILTER_ALL) + enum Dav1dDecodeFrameType decode_frame_type; ///< frame types to decode (default + ///< DAV1D_DECODEFRAMETYPE_ALL) + uint8_t reserved[16]; ///< reserved for future use +} Dav1dSettings; + +/** + * Get library version. + */ +DAV1D_API const char *dav1d_version(void); + +/** + * Get library API version. + * + * @return A value in the format 0x00XXYYZZ, where XX is the major version, + * YY the minor version, and ZZ the patch version. + * @see DAV1D_API_MAJOR, DAV1D_API_MINOR, DAV1D_API_PATCH + */ +DAV1D_API unsigned dav1d_version_api(void); + +/** + * Initialize settings to default values. + * + * @param s Input settings context. + */ +DAV1D_API void dav1d_default_settings(Dav1dSettings *s); + +/** + * Allocate and open a decoder instance. + * + * @param c_out The decoder instance to open. *c_out will be set to the + * allocated context. + * @param s Input settings context. + * + * @note The context must be freed using dav1d_close() when decoding is + * finished. + * + * @return 0 on success, or < 0 (a negative DAV1D_ERR code) on error. + */ +DAV1D_API int dav1d_open(Dav1dContext **c_out, const Dav1dSettings *s); + +/** + * Parse a Sequence Header OBU from bitstream data. + * + * @param out Output Sequence Header. + * @param buf The data to be parser. + * @param sz Size of the data. + * + * @return + * 0: Success, and out is filled with the parsed Sequence Header + * OBU parameters. + * DAV1D_ERR(ENOENT): No Sequence Header OBUs were found in the buffer. + * Other negative DAV1D_ERR codes: Invalid data in the buffer, invalid passed-in + * arguments, and other errors during parsing. + * + * @note It is safe to feed this function data containing other OBUs than a + * Sequence Header, as they will simply be ignored. If there is more than + * one Sequence Header OBU present, only the last will be returned. + */ +DAV1D_API int dav1d_parse_sequence_header(Dav1dSequenceHeader *out, + const uint8_t *buf, const size_t sz); + +/** + * Feed bitstream data to the decoder, in the form of one or multiple AV1 + * Open Bitstream Units (OBUs). + * + * @param c Input decoder instance. + * @param in Input bitstream data. On success, ownership of the reference is + * passed to the library. + * + * @return + * 0: Success, and the data was consumed. + * DAV1D_ERR(EAGAIN): The data can't be consumed. dav1d_get_picture() should + * be called to get one or more frames before the function + * can consume new data. + * Other negative DAV1D_ERR codes: Error during decoding or because of invalid + * passed-in arguments. The reference remains + * owned by the caller. + */ +DAV1D_API int dav1d_send_data(Dav1dContext *c, Dav1dData *in); + +/** + * Return a decoded picture. + * + * @param c Input decoder instance. + * @param out Output frame. The caller assumes ownership of the returned + * reference. + * + * @return + * 0: Success, and a frame is returned. + * DAV1D_ERR(EAGAIN): Not enough data to output a frame. dav1d_send_data() + * should be called with new input. + * Other negative DAV1D_ERR codes: Error during decoding or because of invalid + * passed-in arguments. + * + * @note To drain buffered frames from the decoder (i.e. on end of stream), + * call this function until it returns DAV1D_ERR(EAGAIN). + * + * @code{.c} + * Dav1dData data = { 0 }; + * Dav1dPicture p = { 0 }; + * int res; + * + * read_data(&data); + * do { + * res = dav1d_send_data(c, &data); + * // Keep going even if the function can't consume the current data + * packet. It eventually will after one or more frames have been + * returned in this loop. + * if (res < 0 && res != DAV1D_ERR(EAGAIN)) + * free_and_abort(); + * res = dav1d_get_picture(c, &p); + * if (res < 0) { + * if (res != DAV1D_ERR(EAGAIN)) + * free_and_abort(); + * } else + * output_and_unref_picture(&p); + * // Stay in the loop as long as there's data to consume. + * } while (data.sz || read_data(&data) == SUCCESS); + * + * // Handle EOS by draining all buffered frames. + * do { + * res = dav1d_get_picture(c, &p); + * if (res < 0) { + * if (res != DAV1D_ERR(EAGAIN)) + * free_and_abort(); + * } else + * output_and_unref_picture(&p); + * } while (res == 0); + * @endcode + */ +DAV1D_API int dav1d_get_picture(Dav1dContext *c, Dav1dPicture *out); + +/** + * Apply film grain to a previously decoded picture. If the picture contains no + * film grain metadata, then this function merely returns a new reference. + * + * @param c Input decoder instance. + * @param out Output frame. The caller assumes ownership of the returned + * reference. + * @param in Input frame. No ownership is transferred. + * + * @return + * 0: Success, and a frame is returned. + * Other negative DAV1D_ERR codes: Error due to lack of memory or because of + * invalid passed-in arguments. + * + * @note If `Dav1dSettings.apply_grain` is true, film grain was already applied + * by `dav1d_get_picture`, and so calling this function leads to double + * application of film grain. Users should only call this when needed. + */ +DAV1D_API int dav1d_apply_grain(Dav1dContext *c, Dav1dPicture *out, + const Dav1dPicture *in); + +/** + * Close a decoder instance and free all associated memory. + * + * @param c_out The decoder instance to close. *c_out will be set to NULL. + */ +DAV1D_API void dav1d_close(Dav1dContext **c_out); + +/** + * Flush all delayed frames in decoder and clear internal decoder state, + * to be used when seeking. + * + * @param c Input decoder instance. + * + * @note Decoding will start only after a valid sequence header OBU is + * delivered to dav1d_send_data(). + * + */ +DAV1D_API void dav1d_flush(Dav1dContext *c); + +enum Dav1dEventFlags { + /** + * The last returned picture contains a reference to a new Sequence Header, + * either because it's the start of a new coded sequence, or the decoder was + * flushed before it was generated. + */ + DAV1D_EVENT_FLAG_NEW_SEQUENCE = 1 << 0, + /** + * The last returned picture contains a reference to a Sequence Header with + * new operating parameters information for the current coded sequence. + */ + DAV1D_EVENT_FLAG_NEW_OP_PARAMS_INFO = 1 << 1, +}; + +/** + * Fetch a combination of DAV1D_EVENT_FLAG_* event flags generated by the decoding + * process. + * + * @param c Input decoder instance. + * @param flags Where to write the flags. + * + * @return 0 on success, or < 0 (a negative DAV1D_ERR code) on error. + * + * @note Calling this function will clear all the event flags currently stored in + * the decoder. + * + */ +DAV1D_API int dav1d_get_event_flags(Dav1dContext *c, enum Dav1dEventFlags *flags); + +/** + * Retrieve the user-provided metadata associated with the input data packet + * for the last decoding error reported to the user, i.e. a negative return + * value (not EAGAIN) from dav1d_send_data() or dav1d_get_picture(). + * + * @param c Input decoder instance. + * @param out Output Dav1dDataProps. On success, the caller assumes ownership of + * the returned reference. + * + * @return 0 on success, or < 0 (a negative DAV1D_ERR code) on error. + */ +DAV1D_API int dav1d_get_decode_error_data_props(Dav1dContext *c, Dav1dDataProps *out); + +/** + * Get the decoder delay, which is the number of internally buffered frames, not + * including reference frames. + * This value is guaranteed to be >= 1 and <= max_frame_delay. + * + * @param s Input settings context. + * + * @return Decoder frame delay on success, or < 0 (a negative DAV1D_ERR code) on + * error. + * + * @note The returned delay is valid only for a Dav1dContext initialized with the + * provided Dav1dSettings. + */ +DAV1D_API int dav1d_get_frame_delay(const Dav1dSettings *s); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* DAV1D_H */ diff --git a/TMessagesProj/jni/ffmpeg/include/dav1d/headers.h b/TMessagesProj/jni/ffmpeg/include/dav1d/headers.h new file mode 100644 index 00000000000..b9037f23de6 --- /dev/null +++ b/TMessagesProj/jni/ffmpeg/include/dav1d/headers.h @@ -0,0 +1,444 @@ +/* + * Copyright © 2018-2020, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DAV1D_HEADERS_H +#define DAV1D_HEADERS_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Constants from Section 3. "Symbols and abbreviated terms" +#define DAV1D_MAX_CDEF_STRENGTHS 8 +#define DAV1D_MAX_OPERATING_POINTS 32 +#define DAV1D_MAX_TILE_COLS 64 +#define DAV1D_MAX_TILE_ROWS 64 +#define DAV1D_MAX_SEGMENTS 8 +#define DAV1D_NUM_REF_FRAMES 8 +#define DAV1D_PRIMARY_REF_NONE 7 +#define DAV1D_REFS_PER_FRAME 7 +#define DAV1D_TOTAL_REFS_PER_FRAME (DAV1D_REFS_PER_FRAME + 1) + +enum Dav1dObuType { + DAV1D_OBU_SEQ_HDR = 1, + DAV1D_OBU_TD = 2, + DAV1D_OBU_FRAME_HDR = 3, + DAV1D_OBU_TILE_GRP = 4, + DAV1D_OBU_METADATA = 5, + DAV1D_OBU_FRAME = 6, + DAV1D_OBU_REDUNDANT_FRAME_HDR = 7, + DAV1D_OBU_PADDING = 15, +}; + +enum Dav1dTxfmMode { + DAV1D_TX_4X4_ONLY, + DAV1D_TX_LARGEST, + DAV1D_TX_SWITCHABLE, + DAV1D_N_TX_MODES, +}; + +enum Dav1dFilterMode { + DAV1D_FILTER_8TAP_REGULAR, + DAV1D_FILTER_8TAP_SMOOTH, + DAV1D_FILTER_8TAP_SHARP, + DAV1D_N_SWITCHABLE_FILTERS, + DAV1D_FILTER_BILINEAR = DAV1D_N_SWITCHABLE_FILTERS, + DAV1D_N_FILTERS, + DAV1D_FILTER_SWITCHABLE = DAV1D_N_FILTERS, +}; + +enum Dav1dAdaptiveBoolean { + DAV1D_OFF = 0, + DAV1D_ON = 1, + DAV1D_ADAPTIVE = 2, +}; + +enum Dav1dRestorationType { + DAV1D_RESTORATION_NONE, + DAV1D_RESTORATION_SWITCHABLE, + DAV1D_RESTORATION_WIENER, + DAV1D_RESTORATION_SGRPROJ, +}; + +enum Dav1dWarpedMotionType { + DAV1D_WM_TYPE_IDENTITY, + DAV1D_WM_TYPE_TRANSLATION, + DAV1D_WM_TYPE_ROT_ZOOM, + DAV1D_WM_TYPE_AFFINE, +}; + +typedef struct Dav1dWarpedMotionParams { + enum Dav1dWarpedMotionType type; + int32_t matrix[6]; + union { + struct { + int16_t alpha, beta, gamma, delta; + } p; + int16_t abcd[4]; + } u; +} Dav1dWarpedMotionParams; + +enum Dav1dPixelLayout { + DAV1D_PIXEL_LAYOUT_I400, ///< monochrome + DAV1D_PIXEL_LAYOUT_I420, ///< 4:2:0 planar + DAV1D_PIXEL_LAYOUT_I422, ///< 4:2:2 planar + DAV1D_PIXEL_LAYOUT_I444, ///< 4:4:4 planar +}; + +enum Dav1dFrameType { + DAV1D_FRAME_TYPE_KEY = 0, ///< Key Intra frame + DAV1D_FRAME_TYPE_INTER = 1, ///< Inter frame + DAV1D_FRAME_TYPE_INTRA = 2, ///< Non key Intra frame + DAV1D_FRAME_TYPE_SWITCH = 3, ///< Switch Inter frame +}; + +enum Dav1dColorPrimaries { + DAV1D_COLOR_PRI_BT709 = 1, + DAV1D_COLOR_PRI_UNKNOWN = 2, + DAV1D_COLOR_PRI_BT470M = 4, + DAV1D_COLOR_PRI_BT470BG = 5, + DAV1D_COLOR_PRI_BT601 = 6, + DAV1D_COLOR_PRI_SMPTE240 = 7, + DAV1D_COLOR_PRI_FILM = 8, + DAV1D_COLOR_PRI_BT2020 = 9, + DAV1D_COLOR_PRI_XYZ = 10, + DAV1D_COLOR_PRI_SMPTE431 = 11, + DAV1D_COLOR_PRI_SMPTE432 = 12, + DAV1D_COLOR_PRI_EBU3213 = 22, + DAV1D_COLOR_PRI_RESERVED = 255, +}; + +enum Dav1dTransferCharacteristics { + DAV1D_TRC_BT709 = 1, + DAV1D_TRC_UNKNOWN = 2, + DAV1D_TRC_BT470M = 4, + DAV1D_TRC_BT470BG = 5, + DAV1D_TRC_BT601 = 6, + DAV1D_TRC_SMPTE240 = 7, + DAV1D_TRC_LINEAR = 8, + DAV1D_TRC_LOG100 = 9, ///< logarithmic (100:1 range) + DAV1D_TRC_LOG100_SQRT10 = 10, ///< lograithmic (100*sqrt(10):1 range) + DAV1D_TRC_IEC61966 = 11, + DAV1D_TRC_BT1361 = 12, + DAV1D_TRC_SRGB = 13, + DAV1D_TRC_BT2020_10BIT = 14, + DAV1D_TRC_BT2020_12BIT = 15, + DAV1D_TRC_SMPTE2084 = 16, ///< PQ + DAV1D_TRC_SMPTE428 = 17, + DAV1D_TRC_HLG = 18, ///< hybrid log/gamma (BT.2100 / ARIB STD-B67) + DAV1D_TRC_RESERVED = 255, +}; + +enum Dav1dMatrixCoefficients { + DAV1D_MC_IDENTITY = 0, + DAV1D_MC_BT709 = 1, + DAV1D_MC_UNKNOWN = 2, + DAV1D_MC_FCC = 4, + DAV1D_MC_BT470BG = 5, + DAV1D_MC_BT601 = 6, + DAV1D_MC_SMPTE240 = 7, + DAV1D_MC_SMPTE_YCGCO = 8, + DAV1D_MC_BT2020_NCL = 9, + DAV1D_MC_BT2020_CL = 10, + DAV1D_MC_SMPTE2085 = 11, + DAV1D_MC_CHROMAT_NCL = 12, ///< Chromaticity-derived + DAV1D_MC_CHROMAT_CL = 13, + DAV1D_MC_ICTCP = 14, + DAV1D_MC_RESERVED = 255, +}; + +enum Dav1dChromaSamplePosition { + DAV1D_CHR_UNKNOWN = 0, + DAV1D_CHR_VERTICAL = 1, ///< Horizontally co-located with luma(0, 0) + ///< sample, between two vertical samples + DAV1D_CHR_COLOCATED = 2, ///< Co-located with luma(0, 0) sample +}; + +typedef struct Dav1dContentLightLevel { + uint16_t max_content_light_level; + uint16_t max_frame_average_light_level; +} Dav1dContentLightLevel; + +typedef struct Dav1dMasteringDisplay { + ///< 0.16 fixed point + uint16_t primaries[3][2]; + ///< 0.16 fixed point + uint16_t white_point[2]; + ///< 24.8 fixed point + uint32_t max_luminance; + ///< 18.14 fixed point + uint32_t min_luminance; +} Dav1dMasteringDisplay; + +typedef struct Dav1dITUTT35 { + uint8_t country_code; + uint8_t country_code_extension_byte; + size_t payload_size; + uint8_t *payload; +} Dav1dITUTT35; + +typedef struct Dav1dSequenceHeader { + /** + * Stream profile, 0 for 8-10 bits/component 4:2:0 or monochrome; + * 1 for 8-10 bits/component 4:4:4; 2 for 4:2:2 at any bits/component, + * or 12 bits/component at any chroma subsampling. + */ + uint8_t profile; + /** + * Maximum dimensions for this stream. In non-scalable streams, these + * are often the actual dimensions of the stream, although that is not + * a normative requirement. + */ + int max_width, max_height; + enum Dav1dPixelLayout layout; ///< format of the picture + enum Dav1dColorPrimaries pri; ///< color primaries (av1) + enum Dav1dTransferCharacteristics trc; ///< transfer characteristics (av1) + enum Dav1dMatrixCoefficients mtrx; ///< matrix coefficients (av1) + enum Dav1dChromaSamplePosition chr; ///< chroma sample position (av1) + /** + * 0, 1 and 2 mean 8, 10 or 12 bits/component, respectively. This is not + * exactly the same as 'hbd' from the spec; the spec's hbd distinguishes + * between 8 (0) and 10-12 (1) bits/component, and another element + * (twelve_bit) to distinguish between 10 and 12 bits/component. To get + * the spec's hbd, use !!our_hbd, and to get twelve_bit, use hbd == 2. + */ + uint8_t hbd; + /** + * Pixel data uses JPEG pixel range ([0,255] for 8bits) instead of + * MPEG pixel range ([16,235] for 8bits luma, [16,240] for 8bits chroma). + */ + uint8_t color_range; + + uint8_t num_operating_points; + struct Dav1dSequenceHeaderOperatingPoint { + uint8_t major_level, minor_level; + uint8_t initial_display_delay; + uint16_t idc; + uint8_t tier; + uint8_t decoder_model_param_present; + uint8_t display_model_param_present; + } operating_points[DAV1D_MAX_OPERATING_POINTS]; + + uint8_t still_picture; + uint8_t reduced_still_picture_header; + uint8_t timing_info_present; + uint32_t num_units_in_tick; + uint32_t time_scale; + uint8_t equal_picture_interval; + uint32_t num_ticks_per_picture; + uint8_t decoder_model_info_present; + uint8_t encoder_decoder_buffer_delay_length; + uint32_t num_units_in_decoding_tick; + uint8_t buffer_removal_delay_length; + uint8_t frame_presentation_delay_length; + uint8_t display_model_info_present; + uint8_t width_n_bits, height_n_bits; + uint8_t frame_id_numbers_present; + uint8_t delta_frame_id_n_bits; + uint8_t frame_id_n_bits; + uint8_t sb128; + uint8_t filter_intra; + uint8_t intra_edge_filter; + uint8_t inter_intra; + uint8_t masked_compound; + uint8_t warped_motion; + uint8_t dual_filter; + uint8_t order_hint; + uint8_t jnt_comp; + uint8_t ref_frame_mvs; + enum Dav1dAdaptiveBoolean screen_content_tools; + enum Dav1dAdaptiveBoolean force_integer_mv; + uint8_t order_hint_n_bits; + uint8_t super_res; + uint8_t cdef; + uint8_t restoration; + uint8_t ss_hor, ss_ver, monochrome; + uint8_t color_description_present; + uint8_t separate_uv_delta_q; + uint8_t film_grain_present; + + // Dav1dSequenceHeaders of the same sequence are required to be + // bit-identical until this offset. See 7.5 "Ordering of OBUs": + // Within a particular coded video sequence, the contents of + // sequence_header_obu must be bit-identical each time the + // sequence header appears except for the contents of + // operating_parameters_info. + struct Dav1dSequenceHeaderOperatingParameterInfo { + uint32_t decoder_buffer_delay; + uint32_t encoder_buffer_delay; + uint8_t low_delay_mode; + } operating_parameter_info[DAV1D_MAX_OPERATING_POINTS]; +} Dav1dSequenceHeader; + +typedef struct Dav1dSegmentationData { + int16_t delta_q; + int8_t delta_lf_y_v, delta_lf_y_h, delta_lf_u, delta_lf_v; + int8_t ref; + uint8_t skip; + uint8_t globalmv; +} Dav1dSegmentationData; + +typedef struct Dav1dSegmentationDataSet { + Dav1dSegmentationData d[DAV1D_MAX_SEGMENTS]; + uint8_t preskip; + int8_t last_active_segid; +} Dav1dSegmentationDataSet; + +typedef struct Dav1dLoopfilterModeRefDeltas { + int8_t mode_delta[2 /* is_zeromv */]; + int8_t ref_delta[DAV1D_TOTAL_REFS_PER_FRAME]; +} Dav1dLoopfilterModeRefDeltas; + +typedef struct Dav1dFilmGrainData { + unsigned seed; + int num_y_points; + uint8_t y_points[14][2 /* value, scaling */]; + int chroma_scaling_from_luma; + int num_uv_points[2]; + uint8_t uv_points[2][10][2 /* value, scaling */]; + int scaling_shift; + int ar_coeff_lag; + int8_t ar_coeffs_y[24]; + int8_t ar_coeffs_uv[2][25 + 3 /* padding for alignment purposes */]; + uint64_t ar_coeff_shift; + int grain_scale_shift; + int uv_mult[2]; + int uv_luma_mult[2]; + int uv_offset[2]; + int overlap_flag; + int clip_to_restricted_range; +} Dav1dFilmGrainData; + +typedef struct Dav1dFrameHeader { + struct { + Dav1dFilmGrainData data; + uint8_t present, update; + } film_grain; ///< film grain parameters + enum Dav1dFrameType frame_type; ///< type of the picture + int width[2 /* { coded_width, superresolution_upscaled_width } */], height; + uint8_t frame_offset; ///< frame number + uint8_t temporal_id; ///< temporal id of the frame for SVC + uint8_t spatial_id; ///< spatial id of the frame for SVC + + uint8_t show_existing_frame; + uint8_t existing_frame_idx; + uint32_t frame_id; + uint32_t frame_presentation_delay; + uint8_t show_frame; + uint8_t showable_frame; + uint8_t error_resilient_mode; + uint8_t disable_cdf_update; + uint8_t allow_screen_content_tools; + uint8_t force_integer_mv; + uint8_t frame_size_override; + uint8_t primary_ref_frame; + uint8_t buffer_removal_time_present; + struct Dav1dFrameHeaderOperatingPoint { + uint32_t buffer_removal_time; + } operating_points[DAV1D_MAX_OPERATING_POINTS]; + uint8_t refresh_frame_flags; + int render_width, render_height; + struct { + uint8_t width_scale_denominator; + uint8_t enabled; + } super_res; + uint8_t have_render_size; + uint8_t allow_intrabc; + uint8_t frame_ref_short_signaling; + int8_t refidx[DAV1D_REFS_PER_FRAME]; + uint8_t hp; + enum Dav1dFilterMode subpel_filter_mode; + uint8_t switchable_motion_mode; + uint8_t use_ref_frame_mvs; + uint8_t refresh_context; + struct { + uint8_t uniform; + uint8_t n_bytes; + uint8_t min_log2_cols, max_log2_cols, log2_cols, cols; + uint8_t min_log2_rows, max_log2_rows, log2_rows, rows; + uint16_t col_start_sb[DAV1D_MAX_TILE_COLS + 1]; + uint16_t row_start_sb[DAV1D_MAX_TILE_ROWS + 1]; + uint16_t update; + } tiling; + struct { + uint8_t yac; + int8_t ydc_delta; + int8_t udc_delta, uac_delta, vdc_delta, vac_delta; + uint8_t qm, qm_y, qm_u, qm_v; + } quant; + struct { + uint8_t enabled, update_map, temporal, update_data; + Dav1dSegmentationDataSet seg_data; + uint8_t lossless[DAV1D_MAX_SEGMENTS], qidx[DAV1D_MAX_SEGMENTS]; + } segmentation; + struct { + struct { + uint8_t present; + uint8_t res_log2; + } q; + struct { + uint8_t present; + uint8_t res_log2; + uint8_t multi; + } lf; + } delta; + uint8_t all_lossless; + struct { + uint8_t level_y[2 /* dir */]; + uint8_t level_u, level_v; + uint8_t mode_ref_delta_enabled; + uint8_t mode_ref_delta_update; + Dav1dLoopfilterModeRefDeltas mode_ref_deltas; + uint8_t sharpness; + } loopfilter; + struct { + uint8_t damping; + uint8_t n_bits; + uint8_t y_strength[DAV1D_MAX_CDEF_STRENGTHS]; + uint8_t uv_strength[DAV1D_MAX_CDEF_STRENGTHS]; + } cdef; + struct { + enum Dav1dRestorationType type[3 /* plane */]; + uint8_t unit_size[2 /* y, uv */]; + } restoration; + enum Dav1dTxfmMode txfm_mode; + uint8_t switchable_comp_refs; + uint8_t skip_mode_allowed, skip_mode_enabled; + int8_t skip_mode_refs[2]; + uint8_t warp_motion; + uint8_t reduced_txtp_set; + Dav1dWarpedMotionParams gmv[DAV1D_REFS_PER_FRAME]; +} Dav1dFrameHeader; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* DAV1D_HEADERS_H */ diff --git a/TMessagesProj/jni/ffmpeg/include/dav1d/picture.h b/TMessagesProj/jni/ffmpeg/include/dav1d/picture.h new file mode 100644 index 00000000000..cc291a4abb9 --- /dev/null +++ b/TMessagesProj/jni/ffmpeg/include/dav1d/picture.h @@ -0,0 +1,157 @@ +/* + * Copyright © 2018-2020, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DAV1D_PICTURE_H +#define DAV1D_PICTURE_H + +#include +#include + +#include "common.h" +#include "headers.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Number of bytes to align AND pad picture memory buffers by, so that SIMD + * implementations can over-read by a few bytes, and use aligned read/write + * instructions. */ +#define DAV1D_PICTURE_ALIGNMENT 64 + +typedef struct Dav1dPictureParameters { + int w; ///< width (in pixels) + int h; ///< height (in pixels) + enum Dav1dPixelLayout layout; ///< format of the picture + int bpc; ///< bits per pixel component (8 or 10) +} Dav1dPictureParameters; + +typedef struct Dav1dPicture { + Dav1dSequenceHeader *seq_hdr; + Dav1dFrameHeader *frame_hdr; + + /** + * Pointers to planar image data (Y is [0], U is [1], V is [2]). The data + * should be bytes (for 8 bpc) or words (for 10 bpc). In case of words + * containing 10 bpc image data, the pixels should be located in the LSB + * bits, so that values range between [0, 1023]; the upper bits should be + * zero'ed out. + */ + void *data[3]; + + /** + * Number of bytes between 2 lines in data[] for luma [0] or chroma [1]. + */ + ptrdiff_t stride[2]; + + Dav1dPictureParameters p; + Dav1dDataProps m; + + /** + * High Dynamic Range Content Light Level metadata applying to this picture, + * as defined in section 5.8.3 and 6.7.3 + */ + Dav1dContentLightLevel *content_light; + /** + * High Dynamic Range Mastering Display Color Volume metadata applying to + * this picture, as defined in section 5.8.4 and 6.7.4 + */ + Dav1dMasteringDisplay *mastering_display; + /** + * Array of ITU-T T.35 metadata as defined in section 5.8.2 and 6.7.2 + */ + Dav1dITUTT35 *itut_t35; + + /** + * Number of ITU-T T35 metadata entries in the array + */ + size_t n_itut_t35; + + uintptr_t reserved[4]; ///< reserved for future use + + struct Dav1dRef *frame_hdr_ref; ///< Dav1dFrameHeader allocation origin + struct Dav1dRef *seq_hdr_ref; ///< Dav1dSequenceHeader allocation origin + struct Dav1dRef *content_light_ref; ///< Dav1dContentLightLevel allocation origin + struct Dav1dRef *mastering_display_ref; ///< Dav1dMasteringDisplay allocation origin + struct Dav1dRef *itut_t35_ref; ///< Dav1dITUTT35 allocation origin + uintptr_t reserved_ref[4]; ///< reserved for future use + struct Dav1dRef *ref; ///< Frame data allocation origin + + void *allocator_data; ///< pointer managed by the allocator +} Dav1dPicture; + +typedef struct Dav1dPicAllocator { + void *cookie; ///< custom data to pass to the allocator callbacks. + /** + * Allocate the picture buffer based on the Dav1dPictureParameters. + * + * The data[0], data[1] and data[2] must be DAV1D_PICTURE_ALIGNMENT byte + * aligned and with a pixel width/height multiple of 128 pixels. Any + * allocated memory area should also be padded by DAV1D_PICTURE_ALIGNMENT + * bytes. + * data[1] and data[2] must share the same stride[1]. + * + * This function will be called on the main thread (the thread which calls + * dav1d_get_picture()). + * + * @param pic The picture to allocate the buffer for. The callback needs to + * fill the picture data[0], data[1], data[2], stride[0] and + * stride[1]. + * The allocator can fill the pic allocator_data pointer with + * a custom pointer that will be passed to + * release_picture_callback(). + * @param cookie Custom pointer passed to all calls. + * + * @note No fields other than data, stride and allocator_data must be filled + * by this callback. + * @return 0 on success. A negative DAV1D_ERR value on error. + */ + int (*alloc_picture_callback)(Dav1dPicture *pic, void *cookie); + /** + * Release the picture buffer. + * + * If frame threading is used, this function may be called by the main + * thread (the thread which calls dav1d_get_picture()) or any of the frame + * threads and thus must be thread-safe. If frame threading is not used, + * this function will only be called on the main thread. + * + * @param pic The picture that was filled by alloc_picture_callback(). + * @param cookie Custom pointer passed to all calls. + */ + void (*release_picture_callback)(Dav1dPicture *pic, void *cookie); +} Dav1dPicAllocator; + +/** + * Release reference to a picture. + */ +DAV1D_API void dav1d_picture_unref(Dav1dPicture *p); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* DAV1D_PICTURE_H */ diff --git a/TMessagesProj/jni/ffmpeg/include/dav1d/version.h b/TMessagesProj/jni/ffmpeg/include/dav1d/version.h new file mode 100644 index 00000000000..43df6039153 --- /dev/null +++ b/TMessagesProj/jni/ffmpeg/include/dav1d/version.h @@ -0,0 +1,50 @@ +/* + * Copyright © 2019-2024, VideoLAN and dav1d authors + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DAV1D_VERSION_H +#define DAV1D_VERSION_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define DAV1D_API_VERSION_MAJOR 7 +#define DAV1D_API_VERSION_MINOR 0 +#define DAV1D_API_VERSION_PATCH 0 + +/** + * Extract version components from the value returned by + * dav1d_version_int() + */ +#define DAV1D_API_MAJOR(v) (((v) >> 16) & 0xFF) +#define DAV1D_API_MINOR(v) (((v) >> 8) & 0xFF) +#define DAV1D_API_PATCH(v) (((v) >> 0) & 0xFF) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* DAV1D_VERSION_H */ diff --git a/TMessagesProj/jni/ffmpeg/x86/libavcodec.a b/TMessagesProj/jni/ffmpeg/x86/libavcodec.a index 002a567a918..2d0c3f64460 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 00c5ac2ef37..43638604271 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 81563020cf8..c618161f3da 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 f03fd350b8c..ddc28b01976 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/libdav1d.a b/TMessagesProj/jni/ffmpeg/x86/libdav1d.a new file mode 100644 index 00000000000..315d475fb17 Binary files /dev/null and b/TMessagesProj/jni/ffmpeg/x86/libdav1d.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86/libswresample.a b/TMessagesProj/jni/ffmpeg/x86/libswresample.a index 00a5203be87..102a354bdf1 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 fb56c176cb7..0dbc4be5fed 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 de3c1db956e..ad2af0d36ca 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 e54555cb9ba..6f7eaf46446 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 f8e54307486..ea06e7b51f2 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 a2e5997b601..9445f641657 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 d982f106773..f8ba4771a5f 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/libdav1d.a b/TMessagesProj/jni/ffmpeg/x86_64/libdav1d.a new file mode 100644 index 00000000000..bae7c5c0a7f Binary files /dev/null and b/TMessagesProj/jni/ffmpeg/x86_64/libdav1d.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86_64/libswresample.a b/TMessagesProj/jni/ffmpeg/x86_64/libswresample.a index 230a286976b..a5523685797 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 3187be8df62..e0dc487b5ef 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 bd0383e8060..4c858b5397b 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/gifvideo.cpp b/TMessagesProj/jni/gifvideo.cpp index c3bc4ff6589..ffe2f8e5b35 100644 --- a/TMessagesProj/jni/gifvideo.cpp +++ b/TMessagesProj/jni/gifvideo.cpp @@ -44,53 +44,6 @@ jmethodID jclass_AnimatedFileDrawableStream_isCanceled; jmethodID jclass_AnimatedFileDrawableStream_isFinishedLoadingFile; jmethodID jclass_AnimatedFileDrawableStream_getFinishedFilePath; -typedef struct H2645NAL { - uint8_t *rbsp_buffer; - int size; - const uint8_t *data; - int size_bits; - int raw_size; - const uint8_t *raw_data; - int type; - int temporal_id; - int nuh_layer_id; - int skipped_bytes; - int skipped_bytes_pos_size; - int *skipped_bytes_pos; - int ref_idc; - GetBitContext gb; -} H2645NAL; - -typedef struct H2645RBSP { - uint8_t *rbsp_buffer; - AVBufferRef *rbsp_buffer_ref; - int rbsp_buffer_alloc_size; - int rbsp_buffer_size; -} H2645RBSP; - -typedef struct H2645Packet { - H2645NAL *nals; - H2645RBSP rbsp; - int nb_nals; - int nals_allocated; - unsigned nal_buffer_size; -} H2645Packet; - -void ff_h2645_packet_uninit(H2645Packet *pkt) { - int i; - for (i = 0; i < pkt->nals_allocated; i++) { - av_freep(&pkt->nals[i].skipped_bytes_pos); - } - av_freep(&pkt->nals); - pkt->nals_allocated = pkt->nal_buffer_size = 0; - if (pkt->rbsp.rbsp_buffer_ref) { - av_buffer_unref(&pkt->rbsp.rbsp_buffer_ref); - pkt->rbsp.rbsp_buffer = NULL; - } else - av_freep(&pkt->rbsp.rbsp_buffer); - pkt->rbsp.rbsp_buffer_alloc_size = pkt->rbsp.rbsp_buffer_size = 0; -} - typedef struct VideoInfo { ~VideoInfo() { @@ -145,7 +98,6 @@ typedef struct VideoInfo { fd = -1; } - ff_h2645_packet_uninit(&h2645Packet); av_packet_unref(&orig_pkt); video_stream_idx = -1; @@ -171,8 +123,6 @@ typedef struct VideoInfo { bool dropFrames = false; - H2645Packet h2645Packet = {nullptr}; - int32_t dst_linesize[1]; struct SwsContext *sws_ctx = nullptr; @@ -252,477 +202,28 @@ int open_codec_context(int *stream_idx, AVCodecContext **dec_ctx, AVFormatContex return 0; } -#define MAX_MBPAIR_SIZE (256*1024) - -int ff_h2645_extract_rbsp(const uint8_t *src, int length, H2645RBSP *rbsp, H2645NAL *nal) -{ - int i, si, di; - uint8_t *dst; - - nal->skipped_bytes = 0; -#define STARTCODE_TEST \ - if (i + 2 < length && src[i + 1] == 0 && src[i + 2] <= 3) { \ - if (src[i + 2] != 3 && src[i + 2] != 0) { \ - /* startcode, so we must be past the end */ \ - length = i; \ - } \ - break; \ - } - - for (i = 0; i + 1 < length; i += 2) { - if (src[i]) - continue; - if (i > 0 && src[i - 1] == 0) - i--; - STARTCODE_TEST; - } - - if (i > length) - i = length; - - nal->rbsp_buffer = &rbsp->rbsp_buffer[rbsp->rbsp_buffer_size]; - dst = nal->rbsp_buffer; - - memcpy(dst, src, i); - si = di = i; - while (si + 2 < length) { - if (src[si + 2] > 3) { - dst[di++] = src[si++]; - dst[di++] = src[si++]; - } else if (src[si] == 0 && src[si + 1] == 0 && src[si + 2] != 0) { - if (src[si + 2] == 3) { - dst[di++] = 0; - dst[di++] = 0; - si += 3; - - if (nal->skipped_bytes_pos) { - nal->skipped_bytes++; - if (nal->skipped_bytes_pos_size < nal->skipped_bytes) { - nal->skipped_bytes_pos_size *= 2; - av_reallocp_array(&nal->skipped_bytes_pos, - nal->skipped_bytes_pos_size, - sizeof(*nal->skipped_bytes_pos)); - if (!nal->skipped_bytes_pos) { - nal->skipped_bytes_pos_size = 0; - return AVERROR(ENOMEM); - } - } - if (nal->skipped_bytes_pos) - nal->skipped_bytes_pos[nal->skipped_bytes-1] = di - 1; - } - continue; - } else // next start code - goto nsc; - } - - dst[di++] = src[si++]; - } - while (si < length) - dst[di++] = src[si++]; - - nsc: - memset(dst + di, 0, AV_INPUT_BUFFER_PADDING_SIZE); - - nal->data = dst; - nal->size = di; - nal->raw_data = src; - nal->raw_size = si; - rbsp->rbsp_buffer_size += si; - - return si; -} - -static inline int get_nalsize(int nal_length_size, const uint8_t *buf, int buf_size, int *buf_index) { - int i, nalsize = 0; - if (*buf_index >= buf_size - nal_length_size) { - return AVERROR(EAGAIN); - } - for (i = 0; i < nal_length_size; i++) - nalsize = ((unsigned)nalsize << 8) | buf[(*buf_index)++]; - if (nalsize <= 0 || nalsize > buf_size - *buf_index) { - return AVERROR_INVALIDDATA; - } - return nalsize; -} - -static int find_next_start_code(const uint8_t *buf, const uint8_t *next_avc) { - int i = 0; - if (buf + 3 >= next_avc) - return next_avc - buf; - while (buf + i + 3 < next_avc) { - if (buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1) - break; - i++; - } - return i + 3; -} - -static int get_bit_length(H2645NAL *nal, int skip_trailing_zeros) { - int size = nal->size; - int v; - - while (skip_trailing_zeros && size > 0 && nal->data[size - 1] == 0) - size--; - - if (!size) - return 0; - - v = nal->data[size - 1]; - - if (size > INT_MAX / 8) - return AVERROR(ERANGE); - size *= 8; - - /* remove the stop bit and following trailing zeros, - * or nothing for damaged bitstreams */ - if (v) - size -= ff_ctz(v) + 1; - - return size; -} - -static void alloc_rbsp_buffer(H2645RBSP *rbsp, unsigned int size) { - int min_size = size; - - if (size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) - goto fail; - size += AV_INPUT_BUFFER_PADDING_SIZE; - - if (rbsp->rbsp_buffer_alloc_size >= size && - (!rbsp->rbsp_buffer_ref || av_buffer_is_writable(rbsp->rbsp_buffer_ref))) { - memset(rbsp->rbsp_buffer + min_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); - return; - } - - size = FFMIN(size + size / 16 + 32, INT_MAX); - - if (rbsp->rbsp_buffer_ref) - av_buffer_unref(&rbsp->rbsp_buffer_ref); - else - av_free(rbsp->rbsp_buffer); - - rbsp->rbsp_buffer = (uint8_t *) av_mallocz(size); - if (!rbsp->rbsp_buffer) - goto fail; - rbsp->rbsp_buffer_alloc_size = size; - - return; - - fail: - rbsp->rbsp_buffer_alloc_size = 0; - if (rbsp->rbsp_buffer_ref) { - av_buffer_unref(&rbsp->rbsp_buffer_ref); - rbsp->rbsp_buffer = NULL; - } else - av_freep(&rbsp->rbsp_buffer); - - return; -} - -static int h264_parse_nal_header(H2645NAL *nal) { - GetBitContext *gb = &nal->gb; - - if (get_bits1(gb) != 0) - return AVERROR_INVALIDDATA; - - nal->ref_idc = get_bits(gb, 2); - nal->type = get_bits(gb, 5); - - return 1; -} - -int ff_h2645_packet_split(H2645Packet *pkt, const uint8_t *buf, int length, int is_nalff, int nal_length_size) { - GetByteContext bc; - int consumed, ret = 0; - int next_avc = is_nalff ? 0 : length; - int64_t padding = MAX_MBPAIR_SIZE; - - bytestream2_init(&bc, buf, length); - alloc_rbsp_buffer(&pkt->rbsp, length + padding); - - if (!pkt->rbsp.rbsp_buffer) - return AVERROR(ENOMEM); - - pkt->rbsp.rbsp_buffer_size = 0; - pkt->nb_nals = 0; - while (bytestream2_get_bytes_left(&bc) >= 4) { - H2645NAL *nal; - int extract_length = 0; - int skip_trailing_zeros = 1; - - if (bytestream2_tell(&bc) == next_avc) { - int i = 0; - extract_length = get_nalsize(nal_length_size, bc.buffer, bytestream2_get_bytes_left(&bc), &i); - if (extract_length < 0) - return extract_length; - - bytestream2_skip(&bc, nal_length_size); - - next_avc = bytestream2_tell(&bc) + extract_length; - } else { - int buf_index; - buf_index = find_next_start_code(bc.buffer, buf + next_avc); - bytestream2_skip(&bc, buf_index); - if (!bytestream2_get_bytes_left(&bc)) { - if (pkt->nb_nals > 0) { - return 0; - } else { - return AVERROR_INVALIDDATA; - } - } - extract_length = FFMIN(bytestream2_get_bytes_left(&bc), next_avc - bytestream2_tell(&bc)); - if (bytestream2_tell(&bc) >= next_avc) { - bytestream2_skip(&bc, next_avc - bytestream2_tell(&bc)); - continue; - } - } - - if (pkt->nals_allocated < pkt->nb_nals + 1) { - int new_size = pkt->nals_allocated + 1; - void *tmp; - - if (new_size >= INT_MAX / sizeof(*pkt->nals)) - return AVERROR(ENOMEM); - - tmp = av_fast_realloc(pkt->nals, &pkt->nal_buffer_size, new_size * sizeof(*pkt->nals)); - if (!tmp) - return AVERROR(ENOMEM); - - pkt->nals = (H2645NAL *) tmp; - memset(pkt->nals + pkt->nals_allocated, 0, sizeof(*pkt->nals)); - - nal = &pkt->nals[pkt->nb_nals]; - nal->skipped_bytes_pos_size = 1024; - nal->skipped_bytes_pos = (int *) av_malloc_array(nal->skipped_bytes_pos_size, sizeof(*nal->skipped_bytes_pos)); - if (!nal->skipped_bytes_pos) - return AVERROR(ENOMEM); - - pkt->nals_allocated = new_size; - } - nal = &pkt->nals[pkt->nb_nals]; - - consumed = ff_h2645_extract_rbsp(bc.buffer, extract_length, &pkt->rbsp, nal); - if (consumed < 0) - return consumed; - - pkt->nb_nals++; - - bytestream2_skip(&bc, consumed); - - /* see commit 3566042a0 */ - if (bytestream2_get_bytes_left(&bc) >= 4 && - bytestream2_peek_be32(&bc) == 0x000001E0) - skip_trailing_zeros = 0; - - nal->size_bits = get_bit_length(nal, skip_trailing_zeros); - - ret = init_get_bits(&nal->gb, nal->data, nal->size_bits); - if (ret < 0) - return ret; - - ret = h264_parse_nal_header(nal); - if (ret <= 0 || nal->size <= 0 || nal->size_bits <= 0) { - pkt->nb_nals--; - } - } - - return 0; -} - -#define MAX_SPS_COUNT 32 - -const uint8_t ff_zigzag_direct[64] = { - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63 -}; - -const uint8_t ff_zigzag_scan[16+1] = { - 0 + 0 * 4, 1 + 0 * 4, 0 + 1 * 4, 0 + 2 * 4, - 1 + 1 * 4, 2 + 0 * 4, 3 + 0 * 4, 2 + 1 * 4, - 1 + 2 * 4, 0 + 3 * 4, 1 + 3 * 4, 2 + 2 * 4, - 3 + 1 * 4, 3 + 2 * 4, 2 + 3 * 4, 3 + 3 * 4, -}; - -static int decode_scaling_list(GetBitContext *gb, uint8_t *factors, int size) { - int i, last = 8, next = 8; - const uint8_t *scan = size == 16 ? ff_zigzag_scan : ff_zigzag_direct; - if (!get_bits1(gb)) { - - } else { - for (i = 0; i < size; i++) { - if (next) { - int v = get_se_golomb(gb); - if (v < -128 || v > 127) { - return AVERROR_INVALIDDATA; - } - next = (last + v) & 0xff; - } - if (!i && !next) { /* matrix not written, we use the preset one */ - break; - } - last = factors[scan[i]] = next ? next : last; - } - } - return 0; -} - -static int decode_scaling_matrices(GetBitContext *gb, int chroma_format_idc, uint8_t(*scaling_matrix4)[16], uint8_t(*scaling_matrix8)[64]) { - int ret = 0; - if (get_bits1(gb)) { - ret |= decode_scaling_list(gb, scaling_matrix4[0], 16); // Intra, Y - ret |= decode_scaling_list(gb, scaling_matrix4[1], 16); // Intra, Cr - ret |= decode_scaling_list(gb, scaling_matrix4[2], 16); // Intra, Cb - ret |= decode_scaling_list(gb, scaling_matrix4[3], 16); // Inter, Y - ret |= decode_scaling_list(gb, scaling_matrix4[4], 16); // Inter, Cr - ret |= decode_scaling_list(gb, scaling_matrix4[5], 16); // Inter, Cb - - ret |= decode_scaling_list(gb, scaling_matrix8[0], 64); // Intra, Y - ret |= decode_scaling_list(gb, scaling_matrix8[3], 64); // Inter, Y - if (chroma_format_idc == 3) { - ret |= decode_scaling_list(gb, scaling_matrix8[1], 64); // Intra, Cr - ret |= decode_scaling_list(gb, scaling_matrix8[4], 64); // Inter, Cr - ret |= decode_scaling_list(gb, scaling_matrix8[2], 64); // Intra, Cb - ret |= decode_scaling_list(gb, scaling_matrix8[5], 64); // Inter, Cb - } - if (!ret) - ret = 1; - } - - return ret; -} - -int ff_h264_decode_seq_parameter_set(GetBitContext *gb, int &width, int &height) { - int profile_idc, level_idc, constraint_set_flags = 0; - unsigned int sps_id; - int i, log2_max_frame_num_minus4; - int ret; - - profile_idc = get_bits(gb, 8); - constraint_set_flags |= get_bits1(gb) << 0; - constraint_set_flags |= get_bits1(gb) << 1; - constraint_set_flags |= get_bits1(gb) << 2; - constraint_set_flags |= get_bits1(gb) << 3; - constraint_set_flags |= get_bits1(gb) << 4; - constraint_set_flags |= get_bits1(gb) << 5; - skip_bits(gb, 2); - level_idc = get_bits(gb, 8); - sps_id = get_ue_golomb_31(gb); - - if (sps_id >= MAX_SPS_COUNT) { - return false; - } - - if (profile_idc == 100 || // High profile - profile_idc == 110 || // High10 profile - profile_idc == 122 || // High422 profile - profile_idc == 244 || // High444 Predictive profile - profile_idc == 44 || // Cavlc444 profile - profile_idc == 83 || // Scalable Constrained High profile (SVC) - profile_idc == 86 || // Scalable High Intra profile (SVC) - profile_idc == 118 || // Stereo High profile (MVC) - profile_idc == 128 || // Multiview High profile (MVC) - profile_idc == 138 || // Multiview Depth High profile (MVCD) - profile_idc == 144) { // old High444 profile - int chroma_format_idc = get_ue_golomb_31(gb); - if (chroma_format_idc > 3U) { - return false; - } else if (chroma_format_idc == 3) { - int residual_color_transform_flag = get_bits1(gb); - if (residual_color_transform_flag) { - return false; - } - } - int bit_depth_luma = get_ue_golomb(gb) + 8; - int bit_depth_chroma = get_ue_golomb(gb) + 8; - if (bit_depth_chroma != bit_depth_luma) { - return false; - } - if (bit_depth_luma < 8 || bit_depth_luma > 14 || bit_depth_chroma < 8 || bit_depth_chroma > 14) { - return false; - } - get_bits1(gb); - uint8_t scaling_matrix4[6][16]; - uint8_t scaling_matrix8[6][64]; - ret = decode_scaling_matrices(gb, chroma_format_idc, scaling_matrix4, scaling_matrix8); - if (ret < 0) - return false; - } - - get_ue_golomb(gb); - - int poc_type = get_ue_golomb_31(gb); - - if (poc_type == 0) { - unsigned t = get_ue_golomb(gb); - if (t > 12) { - return false; - } - } else if (poc_type == 1) { - get_bits1(gb); - int offset_for_non_ref_pic = get_se_golomb_long(gb); - int offset_for_top_to_bottom_field = get_se_golomb_long(gb); - - if (offset_for_non_ref_pic == INT32_MIN || offset_for_top_to_bottom_field == INT32_MIN) { - return false; - } - - int poc_cycle_length = get_ue_golomb(gb); - - if ((unsigned) poc_cycle_length >= 256) { - return false; - } - - for (i = 0; i < poc_cycle_length; i++) { - int offset_for_ref_frame = get_se_golomb_long(gb); - if (offset_for_ref_frame == INT32_MIN) { - return false; - } - } - } else if (poc_type != 2) { - return false; - } - - get_ue_golomb_31(gb); - get_bits1(gb); - int mb_width = get_ue_golomb(gb) + 1; - int mb_height = get_ue_golomb(gb) + 1; - - if (width == 0 || height == 0) { - width = mb_width; - height = mb_height; - } - return mb_width != width || mb_height != height; -} - int decode_packet(VideoInfo *info, int *got_frame) { int ret = 0; int decoded = info->pkt.size; *got_frame = 0; if (info->pkt.stream_index == info->video_stream_idx) { - if (info->video_stream->codecpar->codec_id == AV_CODEC_ID_H264 && decoded > 0) { - ff_h2645_packet_split(&info->h2645Packet, info->pkt.data, info->pkt.size, 1, 4); - for (int i = 0; i < info->h2645Packet.nb_nals; i++) { - H2645NAL *nal = &info->h2645Packet.nals[i]; - switch (nal->type) { - case 7: { - GetBitContext tmp_gb = nal->gb; - info->dropFrames = ff_h264_decode_seq_parameter_set(&tmp_gb, info->firstWidth, info->firstHeight); - } - } - } - } - if (!info->dropFrames) { - ret = avcodec_decode_video2(info->video_dec_ctx, info->frame, got_frame, &info->pkt); - if (ret != 0) { + while (decoded > 0) { + ret = avcodec_send_packet(info->video_dec_ctx, &info->pkt); + if (ret < 0 && ret != AVERROR(EAGAIN)) { return ret; } + if (ret >= 0) { + ret = avcodec_receive_frame(info->video_dec_ctx, info->frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + return 0; + } else if (ret < 0) { + return ret; + } + *got_frame = 1; + return info->pkt.size; + } + decoded = info->pkt.size; } } @@ -1106,7 +607,13 @@ extern "C" JNIEXPORT void JNICALL Java_org_telegram_ui_Components_AnimatedFileDr info->seeking = true; } -extern "C" JNIEXPORT void JNICALL Java_org_telegram_ui_Components_AnimatedFileDrawable_seekToMs(JNIEnv *env, jclass clazz, jlong ptr, jlong ms, jboolean precise) { +void push_time(JNIEnv *env, VideoInfo* info, jintArray data) { + jint *dataArr = env->GetIntArrayElements(data, 0); + dataArr[3] = (jint) (1000 * info->frame->best_effort_timestamp * av_q2d(info->video_stream->time_base)); + env->ReleaseIntArrayElements(data, dataArr, 0); +} + +extern "C" JNIEXPORT void JNICALL Java_org_telegram_ui_Components_AnimatedFileDrawable_seekToMs(JNIEnv *env, jclass clazz, jlong ptr, jlong ms, jintArray data, jboolean precise) { if (ptr == NULL) { return; } @@ -1120,6 +627,7 @@ extern "C" JNIEXPORT void JNICALL Java_org_telegram_ui_Components_AnimatedFileDr } else { avcodec_flush_buffers(info->video_dec_ctx); if (!precise) { + push_time(env, info, data); return; } int got_frame = 0; @@ -1151,14 +659,17 @@ extern "C" JNIEXPORT void JNICALL Java_org_telegram_ui_Components_AnimatedFileDr info->pkt.size = 0; ret = decode_packet(info, &got_frame); if (ret < 0) { + push_time(env, info, data); return; } if (got_frame == 0) { av_seek_frame(info->fmt_ctx, info->video_stream_idx, 0, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME); + push_time(env, info, data); return; } } if (ret < 0) { + push_time(env, info, data); return; } if (got_frame) { @@ -1172,12 +683,14 @@ extern "C" JNIEXPORT void JNICALL Java_org_telegram_ui_Components_AnimatedFileDr } av_frame_unref(info->frame); if (finished) { + push_time(env, info, data); return; } } tries--; } } + push_time(env, info, data); } uint32_t premultiply_channel_value(const uint32_t pixel, const uint8_t offset, const float normalizedAlpha) { @@ -1186,6 +699,10 @@ uint32_t premultiply_channel_value(const uint32_t pixel, const uint8_t offset, c } static inline void writeFrameToBitmap(JNIEnv *env, VideoInfo *info, jintArray data, jobject bitmap, jint stride) { + if (env->IsSameObject(bitmap, NULL)) { + push_time(env, info, data); + return; + } jint *dataArr = env->GetIntArrayElements(data, 0); int32_t wantedWidth; int32_t wantedHeight; diff --git a/TMessagesProj/jni/tgnet/ConnectionsManager.cpp b/TMessagesProj/jni/tgnet/ConnectionsManager.cpp index 958a0710b91..48e0c196e61 100644 --- a/TMessagesProj/jni/tgnet/ConnectionsManager.cpp +++ b/TMessagesProj/jni/tgnet/ConnectionsManager.cpp @@ -576,6 +576,10 @@ int32_t ConnectionsManager::getCurrentTime() { return (int32_t) (getCurrentTimeMillis() / 1000) + timeDifference; } +int32_t ConnectionsManager::getCurrentPingTime() { + return (int32_t) currentPingTimeLive; +} + uint32_t ConnectionsManager::getCurrentDatacenterId() { Datacenter *datacenter = getDatacenterWithId(DEFAULT_DATACENTER_ID); return datacenter != nullptr ? datacenter->getDatacenterId() : INT_MAX; @@ -1159,6 +1163,8 @@ void ConnectionsManager::processServerResponse(TLObject *message, int64_t messag if (!registeredForInternalPush) { registerForInternalPushUpdates(); } + int32_t diff = getCurrentTimeMonotonicMillis() - sendingPushPingTime; + currentPingTimeLive = (diff + currentPingTimeLive) / 2; if (LOGS_ENABLED) DEBUG_D("connection(%p, account%u, dc%u, type %d) received push ping", connection, instanceNum, datacenter->getDatacenterId(), connection->getConnectionType()); sendingPushPing = false; } else { @@ -1191,7 +1197,7 @@ void ConnectionsManager::processServerResponse(TLObject *message, int64_t messag } } else if (response->ping_id == lastPingId) { int32_t diff = (int32_t) (getCurrentTimeMonotonicMillis() / 1000) - pingTime; - + currentPingTimeLive = ((getCurrentTimeMonotonicMillis() - pingTimeMs) + currentPingTimeLive) / 2; if (abs(diff) < 10) { currentPingTime = (diff + currentPingTime) / 2; if (messageId != 0) { @@ -1755,9 +1761,11 @@ void ConnectionsManager::sendPing(Datacenter *datacenter, bool usePushConnection request->ping_id = ++lastPingId; if (usePushConnection) { request->disconnect_delay = 60 * 7; + sendingPushPingTime = getCurrentTimeMonotonicMillis(); } else { request->disconnect_delay = testBackend ? 10 : 35; - pingTime = (int32_t) (getCurrentTimeMonotonicMillis() / 1000); + pingTimeMs = getCurrentTimeMonotonicMillis(); + pingTime = (int32_t) (pingTimeMs / 1000); } auto networkMessage = new NetworkMessage(); @@ -2533,12 +2541,12 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t )) && !request->awaitingIntegrityCheck) { if (!forceThisRequest && request->connectionToken > 0) { if ((request->connectionType & ConnectionTypeGeneric || request->connectionType & ConnectionTypeTemp) && request->connectionToken == connection->getConnectionToken()) { - if (LOGS_ENABLED) DEBUG_D("request token is valid, not retrying %s (%p)", typeInfo.name(), request->rawRequest); +// if (LOGS_ENABLED) DEBUG_D("request token is valid, not retrying %s (%p)", typeInfo.name(), request->rawRequest); iter++; continue; } else { if (connection->getConnectionToken() != 0 && request->connectionToken == connection->getConnectionToken()) { - if (LOGS_ENABLED) DEBUG_D("request download token is valid, not retrying %s (%p)", typeInfo.name(), request->rawRequest); +// if (LOGS_ENABLED) DEBUG_D("request download token is valid, not retrying %s (%p)", typeInfo.name(), request->rawRequest); iter++; continue; } diff --git a/TMessagesProj/jni/tgnet/ConnectionsManager.h b/TMessagesProj/jni/tgnet/ConnectionsManager.h index 893fd96cbfc..1937118a354 100644 --- a/TMessagesProj/jni/tgnet/ConnectionsManager.h +++ b/TMessagesProj/jni/tgnet/ConnectionsManager.h @@ -46,6 +46,7 @@ class ConnectionsManager { int64_t getCurrentTimeMillis(); int64_t getCurrentTimeMonotonicMillis(); int32_t getCurrentTime(); + int32_t getCurrentPingTime(); uint32_t getCurrentDatacenterId(); bool isTestBackend(); int32_t getTimeDifference(); @@ -142,6 +143,7 @@ class ConnectionsManager { std::map datacenters; std::map> quickAckIdToRequestIds; int32_t pingTime; + int64_t pingTimeMs; bool testBackend = false; bool clientBlocked = true; std::string lastInitSystemLangcode = ""; @@ -150,9 +152,11 @@ class ConnectionsManager { uint32_t movingToDatacenterId = DEFAULT_DATACENTER_ID; int64_t pushSessionId = 0; int32_t currentPingTime = 0; + int32_t currentPingTimeLive = 0; bool registeringForPush = false; int64_t lastPushPingTime = 0; int32_t nextPingTimeOffset = 60000 * 3; + int64_t sendingPushPingTime = 0; bool sendingPushPing = false; bool sendingPing = false; bool updatingDcSettings = false; diff --git a/TMessagesProj/proguard-rules.pro b/TMessagesProj/proguard-rules.pro index dead1442f70..dc6281d6419 100644 --- a/TMessagesProj/proguard-rules.pro +++ b/TMessagesProj/proguard-rules.pro @@ -24,6 +24,9 @@ -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 { *; } +-keepclassmembers class ** { + @android.webkit.JavascriptInterface ; +} # 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/AndroidManifest.xml b/TMessagesProj/src/main/AndroidManifest.xml index a7f886268e7..095287fad07 100644 --- a/TMessagesProj/src/main/AndroidManifest.xml +++ b/TMessagesProj/src/main/AndroidManifest.xml @@ -590,6 +590,8 @@ e + + = maxDurationForQualityDecreaseUs) { // The selected track is a lower quality, but we have sufficient buffer to defer switching - // down for now. + // down for now. maxDurationForQualityDecreaseUs+ ")"); newSelectedIndex = previousSelectedIndex; } } @@ -516,7 +519,7 @@ public int evaluateQueueSize(long playbackPositionUs, List if (playoutBufferedDurationBeforeLastChunkUs < minDurationToRetainAfterDiscardUs) { return queueSize; } - int idealSelectedIndex = determineIdealSelectedIndex(nowMs, getLastChunkDurationUs(queue)); + int idealSelectedIndex = determineIdealSelectedIndex(-1, nowMs, getLastChunkDurationUs(queue)); Format idealFormat = getFormat(idealSelectedIndex); // If chunks contain video, discard from the first chunk after minDurationToRetainAfterDiscardUs // whose resolution and bitrate are both lower than the ideal track, and whose width and height @@ -551,7 +554,7 @@ public int evaluateQueueSize(long playbackPositionUs, List */ @SuppressWarnings("unused") protected boolean canSelectFormat(Format format, int trackBitrate, long effectiveBitrate) { - return trackBitrate <= effectiveBitrate; + return format.cached || trackBitrate <= effectiveBitrate; } /** @@ -586,19 +589,62 @@ protected long getMinDurationToRetainAfterDiscardUs() { * @param chunkDurationUs The duration of a media chunk in microseconds, or {@link C#TIME_UNSET} * if unknown. */ - private int determineIdealSelectedIndex(long nowMs, long chunkDurationUs) { - long effectiveBitrate = getAllocatedBandwidth(chunkDurationUs); - int lowestBitrateAllowedIndex = 0; + private int determineIdealSelectedIndex(int type, long nowMs, long chunkDurationUs) { + final long effectiveBitrate = getAllocatedBandwidth(chunkDurationUs); + FileLog.d("debug_loading_player: determineIdealSelectedIndex: type="+type+" effectiveBitrate=" + effectiveBitrate); + final HashMap formatsByResolution = new HashMap<>(); + final ArrayList formatIndices = new ArrayList<>(); for (int i = 0; i < length; i++) { - if (nowMs == Long.MIN_VALUE || !isBlacklisted(i, nowMs)) { + if (nowMs != Long.MIN_VALUE && isBlacklisted(i, nowMs)) continue; + final Format format = getFormat(i); + final int resolution = Math.max(format.width, format.height); + if (!formatsByResolution.containsKey(resolution)) { + formatsByResolution.put(resolution, i); + formatIndices.add(i); + } else { + final int existingFormatIndex = formatsByResolution.get(resolution); + final Format existingFormat = getFormat(existingFormatIndex); + if (existingFormat.cached && !format.cached) continue; + if ( + !existingFormat.cached && format.cached || + format.bitrate < existingFormat.bitrate + ) { + formatsByResolution.put(resolution, i); + formatIndices.remove((Integer) existingFormatIndex); + formatIndices.add(i); + } + } + } + if (type == 0) { + for (int i : formatIndices) { Format format = getFormat(i); - if (canSelectFormat(format, format.bitrate, effectiveBitrate)) { + if (format.cached) { + FileLog.d("debug_loading_player: determineIdealSelectedIndex: initial setup, choose cached format#" + i); return i; - } else { - lowestBitrateAllowedIndex = i; } } } + int lowestBitrateAllowedIndex = 0; + for (int i : formatIndices) { + Format format = getFormat(i); + FileLog.d("debug_loading_player: determineIdealSelectedIndex: format#" + i + " bitrate=" + format.bitrate + " " + format.width + "x" + format.height + " codecs="+format.codecs+" (cached=" + format.cached + ")"); + if (canSelectFormat(format, format.bitrate, effectiveBitrate)) { +// if (!format.cached && type == 0) { +// for (int j = i + 1; j < formatIndices.size(); ++j) { +// int i2 = formatIndices.get(j); +// if (getFormat(i2).cached) { +// FileLog.d("debug_loading_player: determineIdealSelectedIndex: chose to start with lower but cached format#" + i); +// return i2; +// } +// } +// } + FileLog.d("debug_loading_player: determineIdealSelectedIndex: selected format#" + i); + return i; + } else { + lowestBitrateAllowedIndex = i; + } + } + FileLog.d("debug_loading_player: determineIdealSelectedIndex: selected format#" + lowestBitrateAllowedIndex + " (lowest, nothing is fit)"); return lowestBitrateAllowedIndex; } diff --git a/TMessagesProj/src/main/java/com/google/android/exoplayer2/upstream/BaseDataSource.java b/TMessagesProj/src/main/java/com/google/android/exoplayer2/upstream/BaseDataSource.java index 1e2c4a5bd31..0c1103f1c38 100644 --- a/TMessagesProj/src/main/java/com/google/android/exoplayer2/upstream/BaseDataSource.java +++ b/TMessagesProj/src/main/java/com/google/android/exoplayer2/upstream/BaseDataSource.java @@ -33,7 +33,7 @@ */ public abstract class BaseDataSource implements DataSource { - private final boolean isNetwork; + protected boolean isNetwork; private final ArrayList listeners; private int listenerCount; diff --git a/TMessagesProj/src/main/java/com/google/android/exoplayer2/upstream/DefaultBandwidthMeter.java b/TMessagesProj/src/main/java/com/google/android/exoplayer2/upstream/DefaultBandwidthMeter.java index 7803f316e2f..970df0f33cb 100644 --- a/TMessagesProj/src/main/java/com/google/android/exoplayer2/upstream/DefaultBandwidthMeter.java +++ b/TMessagesProj/src/main/java/com/google/android/exoplayer2/upstream/DefaultBandwidthMeter.java @@ -17,6 +17,8 @@ import android.content.Context; import android.os.Handler; +import android.util.Log; + import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.upstream.BandwidthMeter.EventListener.EventDispatcher; @@ -28,6 +30,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.errorprone.annotations.CanIgnoreReturnValue; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.FileLog; + import java.util.HashMap; import java.util.Map; @@ -405,6 +411,7 @@ public synchronized void onTransferEnd(DataSource source, DataSpec dataSpec, boo if (totalElapsedTimeMs >= ELAPSED_MILLIS_FOR_ESTIMATE || totalBytesTransferred >= BYTES_TRANSFERRED_FOR_ESTIMATE) { bitrateEstimate = (long) slidingPercentile.getPercentile(0.5f); + FileLog.d("debug_loading: bandwidth meter (onTransferEnd), bitrate estimate = " + bitrateEstimate); } maybeNotifyBandwidthSample(sampleElapsedTimeMs, sampleBytesTransferred, bitrateEstimate); sampleStartTimeMs = nowMs; @@ -413,6 +420,26 @@ public synchronized void onTransferEnd(DataSource source, DataSpec dataSpec, boo streamCount--; } + public void onTransfer(long bytes, long duration) { + long nowMs = clock.elapsedRealtime(); + int sampleElapsedTimeMs = (int) (nowMs - sampleStartTimeMs); + totalElapsedTimeMs += sampleElapsedTimeMs; + totalBytesTransferred += bytes; + if (duration > 0 && bytes > 0) { + FileLog.d("debug_loading: bandwidth meter on transfer " + AndroidUtilities.formatFileSize(bytes) + " per " +duration + "ms"); + float bitsPerSecond = (bytes * 8000f) / duration; + slidingPercentile.addSample((int) Math.sqrt(bytes), bitsPerSecond); + if (totalElapsedTimeMs >= ELAPSED_MILLIS_FOR_ESTIMATE + || totalBytesTransferred >= BYTES_TRANSFERRED_FOR_ESTIMATE) { + bitrateEstimate = (long) slidingPercentile.getPercentile(0.5f); + FileLog.d("debug_loading: bandwidth meter (onTransfer), bitrate estimate = " + bitrateEstimate); + } + maybeNotifyBandwidthSample((int) duration, bytes, bitrateEstimate); + sampleStartTimeMs = nowMs; + sampleBytesTransferred = 0; + } + } + private synchronized void onNetworkTypeChanged(@C.NetworkType int networkType) { if (this.networkType != C.NETWORK_TYPE_UNKNOWN && !resetOnNetworkTypeChange) { // Reset on network change disabled. Ignore all updates except the initial one. @@ -469,7 +496,7 @@ private long getInitialBitrateEstimateForNetworkType(@C.NetworkType int networkT } private static boolean isTransferAtFullNetworkSpeed(DataSpec dataSpec, boolean isNetwork) { - return isNetwork && !dataSpec.isFlagSet(DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED); + return isNetwork && (dataSpec == null || !dataSpec.isFlagSet(DataSpec.FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED)); } /** diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java index 647911b2add..73d5f6b4e63 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java @@ -666,21 +666,31 @@ public static SpannableStringBuilder replaceSingleLink(String str, int color, Ru } SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(str); if (index >= 0) { - spannableStringBuilder.setSpan(new ClickableSpan() { - @Override - public void updateDrawState(@NonNull TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); - ds.setColor(color); - } + if (onClick != null) { + spannableStringBuilder.setSpan(new ClickableSpan() { + @Override + public void updateDrawState(@NonNull TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + ds.setColor(color); + } - @Override - public void onClick(@NonNull View view) { - if (onClick != null) { - onClick.run(); + @Override + public void onClick(@NonNull View view) { + if (onClick != null) { + onClick.run(); + } } - } - }, index, index + len, 0); + }, index, index + len, 0); + } else { + spannableStringBuilder.setSpan(new CharacterStyle() { + @Override + public void updateDrawState(@NonNull TextPaint ds) { + ds.setUnderlineText(false); + ds.setColor(color); + } + }, index, index + len, 0); + } } return spannableStringBuilder; } @@ -3202,8 +3212,36 @@ public static SpannableStringBuilder replaceTags(String str, int flag, Object... return new SpannableStringBuilder(str); } + public static SpannableStringBuilder replaceTags(SpannableStringBuilder stringBuilder) { + try { + int start; + int end; + ArrayList bolds = new ArrayList<>(); + while ((start = AndroidUtilities.charSequenceIndexOf(stringBuilder, "**")) != -1) { + stringBuilder.replace(start, start + 2, ""); + end = AndroidUtilities.charSequenceIndexOf(stringBuilder, "**"); + if (end >= 0) { + stringBuilder.replace(end, end + 2, ""); + bolds.add(start); + bolds.add(end); + } + } + SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(stringBuilder); + for (int a = 0; a < bolds.size() / 2; a++) { + spannableStringBuilder.setSpan(new TypefaceSpan(AndroidUtilities.bold()), bolds.get(a * 2), bolds.get(a * 2 + 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + return spannableStringBuilder; + } catch (Exception e) { + FileLog.e(e); + } + return stringBuilder; + } + private static Pattern linksPattern; public static SpannableStringBuilder replaceLinks(String str, Theme.ResourcesProvider resourcesProvider) { + return replaceLinks(str, resourcesProvider, null); + } + public static SpannableStringBuilder replaceLinks(String str, Theme.ResourcesProvider resourcesProvider, Runnable onLinkClick) { if (linksPattern == null) { linksPattern = Pattern.compile("\\[(.+?)\\]\\((.+?)\\)"); } @@ -3220,6 +3258,9 @@ public static SpannableStringBuilder replaceLinks(String str, Theme.ResourcesPro spannable.setSpan(new ClickableSpan() { @Override public void onClick(@NonNull View widget) { + if (onLinkClick != null) { + onLinkClick.run(); + } Browser.openUrl(ApplicationLoader.applicationContext, url); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java index d2779cf106f..e90a38f4bb9 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java @@ -45,6 +45,7 @@ import org.telegram.ui.Adapters.DrawerLayoutAdapter; import org.telegram.ui.Components.ForegroundDetector; import org.telegram.ui.Components.Premium.boosts.BoostRepository; +import org.telegram.ui.IUpdateButton; import org.telegram.ui.IUpdateLayout; import org.telegram.ui.LauncherIconController; @@ -288,6 +289,7 @@ public void onCreate() { } catch (Exception e) { FileLog.e(e); } + FileLog.d("device = manufacturer=" + Build.MANUFACTURER + ", device=" + Build.DEVICE + ", model=" + Build.MODEL + ", product=" + Build.PRODUCT); } if (applicationContext == null) { applicationContext = getApplicationContext(); @@ -616,6 +618,10 @@ public IUpdateLayout takeUpdateLayout(Activity activity, ViewGroup sideMenu, Vie return null; } + public IUpdateButton takeUpdateButton(Context context) { + return null; + } + public TLRPC.Update parseTLUpdate(int constructor) { return null; } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BillingController.java b/TMessagesProj/src/main/java/org/telegram/messenger/BillingController.java index 9b3eaf68d76..e0d8c25735c 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/BillingController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/BillingController.java @@ -130,9 +130,13 @@ public void startConnection() { if (isReady()) { return; } - BillingUtilities.extractCurrencyExp(currencyExpMap); - if (!BuildVars.useInvoiceBilling()) { - billingClient.startConnection(this); + try { + BillingUtilities.extractCurrencyExp(currencyExpMap); + if (!BuildVars.useInvoiceBilling()) { + billingClient.startConnection(this); + } + } catch (Exception e) { + FileLog.e(e); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BirthdayController.java b/TMessagesProj/src/main/java/org/telegram/messenger/BirthdayController.java index 3ae56a9c599..31e27942ab7 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/BirthdayController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/BirthdayController.java @@ -285,6 +285,15 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public boolean isToday(long userId) { + if (state != null && state.contains(userId)) + return true; + final TLRPC.UserFull userFull = MessagesController.getInstance(currentAccount).getUserFull(userId); + if (userFull != null && isToday(userFull.birthday)) + return true; + return false; + } + public static boolean isToday(TLRPC.UserFull userFull) { if (userFull == null) return false; return isToday(userFull.birthday); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BotFullscreenButtons.java b/TMessagesProj/src/main/java/org/telegram/messenger/BotFullscreenButtons.java new file mode 100644 index 00000000000..15485ff149c --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/BotFullscreenButtons.java @@ -0,0 +1,473 @@ +package org.telegram.messenger; + +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.ColorFilter; +import android.graphics.CornerPathEffect; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.RenderEffect; +import android.graphics.RenderNode; +import android.graphics.Shader; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.view.MotionEvent; +import android.view.View; +import android.webkit.WebView; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.ButtonBounce; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.Text; +import org.telegram.ui.GradientClip; + +public class BotFullscreenButtons extends View { + + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint iconPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint iconStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Path backgroundPath = new Path(); + private final Paint downloadPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Path downloadPath = new Path(); + + private final RectF insets = new RectF(); + + private final RectF leftMenu = new RectF(); + private final ButtonBounce nullBounce = new ButtonBounce(null); + private final RectF closeRect = new RectF(); + private final RectF closeRectArea = new RectF(); + private final ButtonBounce closeBounce = new ButtonBounce(this); + private final RectF rightMenu = new RectF(); + private final RectF collapseRect = new RectF(); + private final RectF collapseClickRect = new RectF(); + private final ButtonBounce collapseBounce = new ButtonBounce(this); + private final RectF menuRect = new RectF(); + private final RectF menuClickRect = new RectF(); + private final ButtonBounce menuBounce = new ButtonBounce(this); + + private final long start; + private boolean back; + private final AnimatedFloat animatedBack = new AnimatedFloat(this, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + private boolean preview = true; + private final AnimatedFloat animatedPreview = new AnimatedFloat(this, 0, 420, CubicBezierInterpolator.EASE_OUT_QUINT); + private boolean downloading = false; + private final AnimatedFloat animatedDownloading = new AnimatedFloat(this, 0, 420, CubicBezierInterpolator.EASE_OUT_QUINT); + + private final Text backText, closeText; + private final GradientClip previewClip = new GradientClip(); + private Text previewText; + private Drawable verifiedBackground; + private Drawable verifiedForeground; + + public BotFullscreenButtons(Context context) { + super(context); + this.start = System.currentTimeMillis(); + iconStrokePaint.setStyle(Paint.Style.STROKE); + iconStrokePaint.setStrokeCap(Paint.Cap.ROUND); + iconStrokePaint.setStrokeJoin(Paint.Join.ROUND); + backText = new Text(LocaleController.getString(R.string.BotFullscreenBack), 13, AndroidUtilities.bold()); + closeText = new Text(LocaleController.getString(R.string.BotFullscreenClose), 13, AndroidUtilities.bold()); + + downloadPaint.setPathEffect(new CornerPathEffect(dp(1))); + downloadPath.rewind(); + downloadPath.moveTo(-dpf2(1.33f), dpf2(0.16f)); + downloadPath.lineTo(-dpf2(1.33f), -dpf2(3.5f)); + downloadPath.lineTo(dpf2(1.33f), -dpf2(3.5f)); + downloadPath.lineTo(dpf2(1.33f), dpf2(0.16f)); + downloadPath.lineTo(dpf2(3.5f), dpf2(0.16f)); + downloadPath.lineTo(0, dpf2(3.5f)); + downloadPath.lineTo(-dpf2(3.5f), dpf2(0.16f)); + downloadPath.close(); + } + + public void setInsets(RectF rect) { + insets.set(rect); + } + + public void setInsets(Rect rect) { + insets.set(rect); + } + + private RenderNode blurNode; + + @Override + protected void onDraw(@NonNull Canvas canvas) { + super.onDraw(canvas); + + iconPaint.setColor(0xFFFFFFFF); + iconStrokePaint.setColor(0xFFFFFFFF); + iconStrokePaint.setStrokeWidth(dp(2)); + + backgroundPath.rewind(); + + rightMenu.set(getWidth() - insets.right - dp(8 + 71.66f), insets.top + dp(8), getWidth() - insets.right - dp(8), insets.top + dp(8 + 30)); + collapseRect.set(rightMenu.left, rightMenu.top, rightMenu.centerX(), rightMenu.bottom); + collapseClickRect.set(collapseRect.left - dp(8), collapseRect.top - dp(8), collapseRect.right, collapseRect.bottom + dp(8)); + menuRect.set(rightMenu.centerX(), rightMenu.top, rightMenu.right, rightMenu.bottom); + menuClickRect.set(menuRect.left, menuRect.top - dp(8), menuRect.right + dp(8), menuRect.bottom + dp(8)); + backgroundPath.addRoundRect(rightMenu, dp(15), dp(15), Path.Direction.CW); + + final float back = this.animatedBack.set(this.back); + final float preview = this.animatedPreview.set(this.preview); + final float previewWidth = Math.min(rightMenu.left - dp(18) - (insets.left + dp(8 + 30)), previewText == null ? 0 : previewText.getCurrentWidth() + dp(verifiedBackground != null ? 30 : 12)); + final float leftTextWidth = lerp(lerp(closeText.getCurrentWidth(), backText.getCurrentWidth(), back) + dp(12), previewWidth, preview); + leftMenu.set(insets.left + dp(8), insets.top + dp(8), insets.left + dp(8 + 30) + leftTextWidth, insets.top + dp(8 + 30)); + closeRect.set(leftMenu.left, leftMenu.top, leftMenu.left + dp(30), leftMenu.bottom); + closeRectArea.set(closeRect); + closeRectArea.right = lerp(leftMenu.right, closeRect.left + dp(30), preview); + closeRectArea.inset(-dp(8), -dp(8)); + backgroundPath.addRoundRect(leftMenu, dp(15), dp(15), Path.Direction.CW); + + if (parentRenderNode != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && canvas.isHardwareAccelerated() && (webView == null || webView.getLayerType() == LAYER_TYPE_HARDWARE)) { + if (blurNode == null) { + blurNode = new RenderNode("bot_fullscreen_blur"); + blurNode.setRenderEffect(RenderEffect.createBlurEffect(dp(18), dp(18), Shader.TileMode.CLAMP)); + } + RenderNode parentNode = (RenderNode) parentRenderNode; + final int w = Math.max(1, parentNode.getWidth() - dp(16)); + final int h = Math.max(1, (int) Math.min(insets.top + dp(8 + 8 + 30), parentNode.getHeight())); + + blurNode.setPosition(0, 0, w, h); + final Canvas blurCanvas = blurNode.beginRecording(); + blurCanvas.translate(-dp(8), 0); + blurCanvas.drawRenderNode(parentNode); + blurNode.endRecording(); + canvas.save(); + canvas.clipPath(backgroundPath); + canvas.save(); + canvas.translate(dp(8), 0); + canvas.drawRenderNode(blurNode); + canvas.restore(); + backgroundPaint.setColor(Theme.multAlpha(0xFF000000, .22f)); + canvas.drawPaint(backgroundPaint); + canvas.restore(); + } else { + backgroundPaint.setColor(Theme.multAlpha(0xFF000000, .35f)); + canvas.drawPath(backgroundPath, backgroundPaint); + } + + canvas.save(); + canvas.translate(closeRect.centerX(), closeRect.centerY()); + float s = closeBounce.getScale(0.1f); + canvas.scale(s, s); + canvas.translate(back * -dp(6.5f), 0); + final float backR = lerp((float) dp(4.66f), (float) dp(5.5f), back); + canvas.drawLine(lerp(-backR, 0, back), lerp(-backR, 0, back), +backR, +backR, iconStrokePaint); + canvas.drawLine(lerp(-backR, 0, back), lerp(+backR, 0, back), +backR, -backR, iconStrokePaint); + if (back > 0) { + canvas.drawLine(0, 0, dp(11.6f) * back, 0, iconStrokePaint); + } + canvas.restore(); + + canvas.saveLayerAlpha(leftMenu.left + dp(30) - dp(10), leftMenu.top, leftMenu.right, leftMenu.bottom, 0xFF, Canvas.ALL_SAVE_FLAG); + if (preview > 0 && previewText != null) { + canvas.save(); + canvas.translate(leftMenu.left + dp(30) - previewWidth * (1.0f - preview), leftMenu.centerY()); + previewText.ellipsize(leftMenu.right - dp(verifiedBackground != null ? 30 : 12) - (leftMenu.left + dp(30)) + 2).draw(canvas, 0, 0, 0xFFFFFFFF, preview); + canvas.translate(previewText.getWidth() + dp(5), 0); + final int verifiedIconSize = dp(16); + if (verifiedBackground != null) { + verifiedBackground.setBounds(0, -verifiedIconSize / 2, verifiedIconSize, verifiedIconSize / 2); + verifiedBackground.setAlpha((int) (0x4B * preview)); + verifiedBackground.draw(canvas); + } + if (verifiedForeground != null) { + verifiedForeground.setBounds(0, -verifiedIconSize / 2, verifiedIconSize, verifiedIconSize / 2); + verifiedForeground.setAlpha((int) (0xFF * preview)); + verifiedForeground.draw(canvas); + } + AndroidUtilities.rectTmp.set(leftMenu.left + dp(30) - dp(10), leftMenu.top, leftMenu.left + dp(30), leftMenu.bottom); + previewClip.draw(canvas, AndroidUtilities.rectTmp, GradientClip.RIGHT, 1.0f); + canvas.restore(); + } + if (preview < 1) { + canvas.save(); + s = closeBounce.getScale(0.1f); + canvas.scale(s, s, closeRect.centerX(), closeRect.centerY()); + if ((1.0f - back) > 0) { + closeText.draw(canvas, closeRect.left + dp(30) - dp(12) * back + dp(32) * preview, closeRect.centerY(), 0xFFFFFFFF, (1.0f - back) * (1.0f - preview)); + } + if (back > 0) { + backText.draw(canvas, closeRect.left + dp(30) + dp(12) * (1.0f - back) + dp(32) * preview, closeRect.centerY(), 0xFFFFFFFF, back * (1.0f - preview)); + } + canvas.restore(); + } + canvas.restore(); + + canvas.save(); + canvas.translate(collapseRect.centerX() + dp(2), collapseRect.centerY()); + s = collapseBounce.getScale(0.1f); + canvas.scale(s, s); + final float collapseW = dp(6), collapseH = dp(3f); + canvas.drawLine(-collapseW, -collapseH, 0, collapseH, iconStrokePaint); + canvas.drawLine(0, collapseH, collapseW, -collapseH, iconStrokePaint); + canvas.restore(); + + canvas.save(); + canvas.translate(menuRect.centerX() + dp(1), menuRect.centerY()); + s = menuBounce.getScale(0.1f); + canvas.scale(s, s); + canvas.drawCircle(0, -dp(5), dp(1.66f), iconPaint); + canvas.drawCircle(0, 0, dp(1.66f), iconPaint); + canvas.drawCircle(0, +dp(5), dp(1.66f), iconPaint); + final float downloadAlpha = this.animatedDownloading.set(downloading); + if (downloadAlpha > 0) { + canvas.translate(-dpf2(8.166f), dpf2(3.5f)); + s = .5f + .5f * downloadAlpha; + canvas.scale(s, s); + downloadPaint.setColor(Theme.multAlpha(0xFFFFFFFF, 0.4f)); + canvas.drawPath(downloadPath, downloadPaint); + final float t = ((System.currentTimeMillis() - start) % 450 / 450.0f); + + float from = t, to = .5f + t; + + canvas.save(); + canvas.clipRect(-dp(5), lerp(-dpf2(3.5f), dpf2(3.5f), from), dp(5), lerp(-dpf2(3.5f), dpf2(3.5f), to)); + downloadPaint.setColor(Theme.multAlpha(0xFFFFFFFF, 1.0f)); + canvas.drawPath(downloadPath, downloadPaint); + canvas.restore(); + + if (to > 1) { + from = 0; + to = to - 1; + + canvas.save(); + canvas.clipRect(-dp(5), lerp(-dpf2(3.5f), dpf2(3.5f), from), dp(5), lerp(-dpf2(3.5f), dpf2(3.5f), to)); + downloadPaint.setColor(Theme.multAlpha(0xFFFFFFFF, 1.0f)); + canvas.drawPath(downloadPath, downloadPaint); + canvas.restore(); + } + + invalidate(); + } + canvas.restore(); + } + + public void setDownloading(boolean downloading) { + if (this.downloading == downloading) return; + this.downloading = downloading; + invalidate(); + } + + public void setName(String name, boolean verified) { + previewText = new Text(name, 13, AndroidUtilities.bold()); + if (!verified) { + verifiedBackground = null; + verifiedForeground = null; + } else { + verifiedBackground = getContext().getResources().getDrawable(R.drawable.verified_area).mutate(); + verifiedForeground = getContext().getResources().getDrawable(R.drawable.verified_check).mutate(); + } + } + + private final Runnable hidePreview = () -> setPreview(false, true); + + public void setPreview(boolean preview, boolean animated) { + AndroidUtilities.cancelRunOnUIThread(hidePreview); + this.preview = preview; + if (!animated) { + this.animatedPreview.set(preview, true); + } + invalidate(); + if (preview) { + AndroidUtilities.runOnUIThread(hidePreview, 2500); + } + } + + public Runnable onCloseClickListener; + public Runnable onCollapseClickListener; + public Runnable onMenuClickListener; + public Object parentRenderNode; + public WebView webView; + + public void setOnCloseClickListener(Runnable listener) { + onCloseClickListener = listener; + } + public void setOnCollapseClickListener(Runnable listener) { + onCollapseClickListener = listener; + } + public void setOnMenuClickListener(Runnable listener) { + onMenuClickListener = listener; + } + + public void setParentRenderNode(Object renderNode) { + parentRenderNode = renderNode; + } + public void setWebView(WebView webView) { + this.webView = webView; + } + + int pressed; + private int getButton(MotionEvent e) { + if (closeRectArea.contains(e.getX(), e.getY())) { + return 1; + } else if (collapseClickRect.contains(e.getX(), e.getY())) { + return 2; + } else if (menuClickRect.contains(e.getX(), e.getY())) { + return 3; + } else { + return 0; + } + } + + private ButtonBounce getBounce(int button) { + switch (button) { + case 1: return closeBounce; + case 2: return collapseBounce; + case 3: return menuBounce; + default: return nullBounce; + } + } + + public void setBack(boolean enable) { + setBack(enable, true); + } + public void setBack(boolean enable, boolean animated) { + this.back = enable; + if (!animated) { + this.animatedBack.set(enable); + } + invalidate(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + getBounce(pressed).setPressed(false); + pressed = getButton(event); + getBounce(pressed).setPressed(true); + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (getButton(event) != pressed) { + pressed = 0; + getBounce(pressed).setPressed(false); + } + } else if (event.getAction() == MotionEvent.ACTION_UP) { + if (pressed == 1 && onCloseClickListener != null) { + onCloseClickListener.run(); + } else if (pressed == 2 && onCollapseClickListener != null) { + onCollapseClickListener.run(); + } else if (pressed == 3 && onMenuClickListener != null) { + onMenuClickListener.run(); + } + getBounce(pressed).setPressed(false); + pressed = 0; + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + getBounce(pressed).setPressed(false); + pressed = 0; + } + return pressed != 0; + } + + public static class OptionsIcon extends Drawable { + + private final long start; + private final Drawable drawable; + private final Paint downloadPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Path downloadPath = new Path(); + + private boolean downloading = false; + private final AnimatedFloat animatedDownloading = new AnimatedFloat(this::invalidateSelf, 0, 420, CubicBezierInterpolator.EASE_OUT_QUINT); + + public OptionsIcon(Context context) { + start = System.currentTimeMillis(); + drawable = context.getResources().getDrawable(R.drawable.ic_ab_other).mutate(); + + downloadPaint.setPathEffect(new CornerPathEffect(dp(1))); + downloadPath.rewind(); + downloadPath.moveTo(-dpf2(1.33f), dpf2(0.16f)); + downloadPath.lineTo(-dpf2(1.33f), -dpf2(3.5f)); + downloadPath.lineTo(dpf2(1.33f), -dpf2(3.5f)); + downloadPath.lineTo(dpf2(1.33f), dpf2(0.16f)); + downloadPath.lineTo(dpf2(3.5f), dpf2(0.16f)); + downloadPath.lineTo(0, dpf2(3.5f)); + downloadPath.lineTo(-dpf2(3.5f), dpf2(0.16f)); + downloadPath.close(); + } + + @Override + public void draw(@NonNull Canvas canvas) { + drawable.setBounds(getBounds()); + drawable.draw(canvas); + + final float downloadAlpha = this.animatedDownloading.set(downloading); + if (downloadAlpha > 0) { + canvas.save(); + canvas.translate(getBounds().centerX(), getBounds().centerY()); + + canvas.translate(-dpf2(8.166f), dpf2(5f)); + float s = .5f + .5f * downloadAlpha; + canvas.scale(s, s); + downloadPaint.setColor(Theme.multAlpha(0xFFFFFFFF, 0.4f)); + canvas.drawPath(downloadPath, downloadPaint); + final float t = ((System.currentTimeMillis() - start) % 450 / 450.0f); + + float from = t, to = .5f + t; + + canvas.save(); + canvas.clipRect(-dp(5), lerp(-dpf2(3.5f), dpf2(3.5f), from), dp(5), lerp(-dpf2(3.5f), dpf2(3.5f), to)); + downloadPaint.setColor(Theme.multAlpha(0xFFFFFFFF, 1.0f)); + canvas.drawPath(downloadPath, downloadPaint); + canvas.restore(); + + if (to > 1) { + from = 0; + to = to - 1; + + canvas.save(); + canvas.clipRect(-dp(5), lerp(-dpf2(3.5f), dpf2(3.5f), from), dp(5), lerp(-dpf2(3.5f), dpf2(3.5f), to)); + downloadPaint.setColor(Theme.multAlpha(0xFFFFFFFF, 1.0f)); + canvas.drawPath(downloadPath, downloadPaint); + canvas.restore(); + } + canvas.restore(); + + invalidateSelf(); + } + } + + public void setDownloading(boolean downloading) { + if (this.downloading == downloading) return; + this.downloading = downloading; + invalidateSelf(); + } + + @Override + public void setAlpha(int alpha) { + drawable.setAlpha(alpha); + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + downloadPaint.setColorFilter(colorFilter); + drawable.setColorFilter(colorFilter); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSPARENT; + } + + @Override + public int getIntrinsicWidth() { + return drawable.getIntrinsicWidth(); + } + + @Override + public int getIntrinsicHeight() { + return drawable.getIntrinsicHeight(); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java index c0bb4216d91..22fc644386f 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java @@ -84,6 +84,7 @@ public class ContactsController extends BaseController { private ArrayList addedByPhonePrivacyRules; private ArrayList voiceMessagesRules; private ArrayList birthdayPrivacyRules; + private ArrayList giftsPrivacyRules; private TLRPC.TL_globalPrivacySettings globalPrivacySettings; public final static int PRIVACY_RULES_TYPE_LASTSEEN = 0; @@ -98,8 +99,9 @@ public class ContactsController extends BaseController { public final static int PRIVACY_RULES_TYPE_BIO = 9; public final static int PRIVACY_RULES_TYPE_MESSAGES = 10; public final static int PRIVACY_RULES_TYPE_BIRTHDAY = 11; + public final static int PRIVACY_RULES_TYPE_GIFTS = 12; - public final static int PRIVACY_RULES_TYPE_COUNT = 12; + public final static int PRIVACY_RULES_TYPE_COUNT = 13; private class MyContentObserver extends ContentObserver { @@ -331,6 +333,7 @@ public void cleanup() { profilePhotoPrivacyRules = null; bioPrivacyRules = null; birthdayPrivacyRules = null; + giftsPrivacyRules = null; forwardsPrivacyRules = null; phonePrivacyRules = null; @@ -2690,6 +2693,9 @@ public void loadPrivacySettings() { case PRIVACY_RULES_TYPE_BIRTHDAY: req.key = new TLRPC.TL_inputPrivacyKeyBirthday(); break; + case PRIVACY_RULES_TYPE_GIFTS: + req.key = new TLRPC.TL_inputPrivacyKeyStarGiftsAutoSave(); + break; case PRIVACY_RULES_TYPE_ADDED_BY_PHONE: req.key = new TLRPC.TL_inputPrivacyKeyAddedByPhone(); break; @@ -2725,6 +2731,9 @@ public void loadPrivacySettings() { case PRIVACY_RULES_TYPE_BIRTHDAY: birthdayPrivacyRules = rules.rules; break; + case PRIVACY_RULES_TYPE_GIFTS: + giftsPrivacyRules = rules.rules; + break; case PRIVACY_RULES_TYPE_FORWARDS: forwardsPrivacyRules = rules.rules; break; @@ -2789,6 +2798,8 @@ public ArrayList getPrivacyRules(int type) { return bioPrivacyRules; case PRIVACY_RULES_TYPE_BIRTHDAY: return birthdayPrivacyRules; + case PRIVACY_RULES_TYPE_GIFTS: + return giftsPrivacyRules; case PRIVACY_RULES_TYPE_FORWARDS: return forwardsPrivacyRules; case PRIVACY_RULES_TYPE_PHONE: @@ -2824,6 +2835,9 @@ public void setPrivacyRules(ArrayList rules, int type) { case PRIVACY_RULES_TYPE_BIRTHDAY: birthdayPrivacyRules = rules; break; + case PRIVACY_RULES_TYPE_GIFTS: + giftsPrivacyRules = rules; + break; case PRIVACY_RULES_TYPE_FORWARDS: forwardsPrivacyRules = rules; break; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/DialogObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/DialogObject.java index 3cb7a749b4f..cb2e354a6b6 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/DialogObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/DialogObject.java @@ -16,6 +16,7 @@ import org.telegram.ui.Components.BackupImageView; import java.util.ArrayList; +import java.util.HashSet; public class DialogObject { @@ -161,13 +162,106 @@ public static String setDialogPhotoTitle(BackupImageView imageView, TLObject dia public static String getPublicUsername(TLObject dialog) { if (dialog instanceof TLRPC.Chat) { - return ChatObject.getPublicUsername((TLRPC.Chat) dialog); + TLRPC.Chat chat = (TLRPC.Chat) dialog; + return getPublicUsername(chat.username, chat.usernames, false); + } else if (dialog instanceof TLRPC.User) { + TLRPC.User user = (TLRPC.User) dialog; + return getPublicUsername(user.username, user.usernames, false); + } + return null; + } + + public static String getPublicUsername(TLObject dialog, String query) { + if (dialog instanceof TLRPC.Chat) { + TLRPC.Chat chat = (TLRPC.Chat) dialog; + return query == null ? getPublicUsername(chat.username, chat.usernames, false) : getSimilarPublicUsername(chat.username, chat.usernames, query); } else if (dialog instanceof TLRPC.User) { - return UserObject.getPublicUsername((TLRPC.User) dialog); + TLRPC.User user = (TLRPC.User) dialog; + return query == null ? getPublicUsername(user.username, user.usernames, false) : getSimilarPublicUsername(user.username, user.usernames, query); + } + return null; + } + + public static String getPublicUsername(String username, ArrayList usernames, boolean editable) { + if (!TextUtils.isEmpty(username) && !editable) { + return username; + } + if (usernames != null) { + for (int i = 0; i < usernames.size(); ++i) { + TLRPC.TL_username u = usernames.get(i); + if (u != null && (u.active && !editable || u.editable) && !TextUtils.isEmpty(u.username)) { + return u.username; + } + } + } + if (!TextUtils.isEmpty(username) && editable && (usernames == null || usernames.size() <= 0)) { + return username; } return null; } + public static String getSimilarPublicUsername(String obj_username, ArrayList obj_usernames, String query) { + double bestSimilarity = -1; + String bestUsername = null; + if (obj_usernames != null) { + for (int i = 0; i < obj_usernames.size(); ++i) { + TLRPC.TL_username u = obj_usernames.get(i); + if (u != null && u.active && !TextUtils.isEmpty(u.username)) { + double s = bestSimilarity < 0 ? 0 : similarity(u.username, query); + if (s > bestSimilarity) { + bestSimilarity = s; + bestUsername = u.username; + } + } + } + } + if (!TextUtils.isEmpty(obj_username)) { + double s = bestSimilarity < 0 ? 0 : similarity(obj_username, query); + if (s > bestSimilarity) { + bestSimilarity = s; + bestUsername = obj_username; + } + } + return bestUsername; + } + + public static double similarity(String s1, String s2) { + String longer = s1, shorter = s2; + if (s1.length() < s2.length()) { // longer should always have greater length + longer = s2; shorter = s1; + } + int longerLength = longer.length(); + if (longerLength == 0) { return 1.0; } + return (longerLength - editDistance(longer, shorter)) / (double) longerLength; + } + + public static int editDistance(String s1, String s2) { + s1 = s1.toLowerCase(); + s2 = s2.toLowerCase(); + + int[] costs = new int[s2.length() + 1]; + for (int i = 0; i <= s1.length(); i++) { + int lastValue = i; + for (int j = 0; j <= s2.length(); j++) { + if (i == 0) + costs[j] = j; + else { + if (j > 0) { + int newValue = costs[j - 1]; + if (s1.charAt(i - 1) != s2.charAt(j - 1)) + newValue = Math.min(Math.min(newValue, lastValue), + costs[j]) + 1; + costs[j - 1] = lastValue; + lastValue = newValue; + } + } + } + if (i > 0) + costs[s2.length()] = lastValue; + } + return costs[s2.length()]; + } + public static long getEmojiStatusDocumentId(TLRPC.EmojiStatus emojiStatus) { if (MessagesController.getInstance(UserConfig.selectedAccount).premiumFeaturesBlocked()) { return 0; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/DownloadController.java b/TMessagesProj/src/main/java/org/telegram/messenger/DownloadController.java index 44985c1f9ca..56dac2155c1 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/DownloadController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/DownloadController.java @@ -620,7 +620,7 @@ public boolean canDownloadMedia(MessageObject messageObject) { } if (messageObject.isHiddenSensitive()) return false; - return canDownloadMedia(messageObject.messageOwner) == 1; + return canDownloadMediaInternal(messageObject) == 1; } public boolean canDownloadMedia(int type, long size) { @@ -663,7 +663,97 @@ public int canDownloadMediaType(MessageObject messageObject) { } if (messageObject.isHiddenSensitive()) return 0; - return canDownloadMedia(messageObject.messageOwner); + return canDownloadMediaInternal(messageObject); + } + + private int canDownloadMediaInternal(MessageObject message) { + if (message == null || message.messageOwner == null) return 0; + if (message.messageOwner.media instanceof TLRPC.TL_messageMediaStory) { + return canPreloadStories() ? 2 : 0; + } + TLRPC.Message msg = message.messageOwner; + int type; + boolean isVideo; + if ((isVideo = MessageObject.isVideoMessage(msg)) || MessageObject.isGifMessage(msg) || MessageObject.isRoundVideoMessage(msg) || MessageObject.isGameMessage(msg)) { + type = AUTODOWNLOAD_TYPE_VIDEO; + } else if (MessageObject.isVoiceMessage(msg)) { + type = AUTODOWNLOAD_TYPE_AUDIO; + } else if (MessageObject.isPhoto(msg) || MessageObject.isStickerMessage(msg) || MessageObject.isAnimatedStickerMessage(msg)) { + type = AUTODOWNLOAD_TYPE_PHOTO; + } else if (MessageObject.getDocument(msg) != null) { + type = AUTODOWNLOAD_TYPE_DOCUMENT; + } else { + return 0; + } + int index; + TLRPC.Peer peer = msg.peer_id; + if (peer != null) { + if (peer.user_id != 0) { + if (getContactsController().contactsDict.containsKey(peer.user_id)) { + index = 0; + } else { + index = 1; + } + } else if (peer.chat_id != 0) { + if (msg.from_id instanceof TLRPC.TL_peerUser && getContactsController().contactsDict.containsKey(msg.from_id.user_id)) { + index = 0; + } else { + index = 2; + } + } else { + TLRPC.Chat chat = msg.peer_id.channel_id != 0 ? getMessagesController().getChat(msg.peer_id.channel_id) : null; + if (ChatObject.isChannel(chat) && chat.megagroup) { + if (msg.from_id instanceof TLRPC.TL_peerUser && getContactsController().contactsDict.containsKey(msg.from_id.user_id)) { + index = 0; + } else { + index = 2; + } + } else { + index = 3; + } + } + } else { + index = 1; + } + Preset preset; + int networkType = ApplicationLoader.getAutodownloadNetworkType(); + if (networkType == StatsController.TYPE_WIFI) { + if (!wifiPreset.enabled) { + return 0; + } + preset = getCurrentWiFiPreset(); + + } else if (networkType == StatsController.TYPE_ROAMING) { + if (!roamingPreset.enabled) { + return 0; + } + preset = getCurrentRoamingPreset(); + } else { + if (!mobilePreset.enabled) { + return 0; + } + preset = getCurrentMobilePreset(); + } + int mask = preset.mask[index]; + long maxSize; + if (type == AUTODOWNLOAD_TYPE_AUDIO) { + maxSize = Math.max(512 * 1024, preset.sizes[typeToIndex(type)]); + } else { + maxSize = preset.sizes[typeToIndex(type)]; + } + long size; + if (message.highestQuality != null) { + size = message.highestQuality.document.size; + } else if (message.thumbQuality != null) { + size = message.thumbQuality.document.size; + } else { + size = MessageObject.getMessageSize(msg); + } + if (isVideo && preset.preloadVideo && size > maxSize && maxSize > 2 * 1024 * 1024) { + return (mask & type) != 0 ? 2 : 0; + } else { + return (type == AUTODOWNLOAD_TYPE_PHOTO || size != 0 && size <= maxSize) && (type == AUTODOWNLOAD_TYPE_AUDIO || (mask & type) != 0) ? 1 : 0; + } } public int canDownloadMedia(TLRPC.Message message) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java index 8aab6ea5fa0..5926d046860 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java @@ -60,6 +60,9 @@ public void setStream(FileLoadOperationStream stream, boolean streamPriority, lo if (stream != null && !streamListeners.contains(stream)) { streamListeners.add(stream); } + if (!streamListeners.isEmpty()) { + Utilities.stageQueue.cancelRunnable(cancelAfterNoStreamListeners); + } if (stream != null && state != stateDownloading && state != stateIdle) { stream.newDataAvailable(); } @@ -195,6 +198,7 @@ private PreloadRange(long o, long l) { private boolean started; private int datacenterId; private int initialDatacenterId; + private long documentId; protected TLRPC.InputFileLocation location; private TLRPC.InputWebFileLocation webLocation; private WebFile webFile; @@ -338,7 +342,7 @@ public FileLoadOperation(ImageLocation imageLocation, Object parent, String exte } } else { location = new TLRPC.TL_inputDocumentFileLocation(); - location.id = imageLocation.documentId; + documentId = location.id = imageLocation.documentId; location.volume_id = imageLocation.location.volume_id; location.local_id = imageLocation.location.local_id; location.access_hash = imageLocation.access_hash; @@ -415,7 +419,7 @@ public FileLoadOperation(TLRPC.Document documentLocation, Object parent) { key = documentLocation.key; } else if (documentLocation instanceof TLRPC.TL_document) { location = new TLRPC.TL_inputDocumentFileLocation(); - location.id = documentLocation.id; + documentId = location.id = documentLocation.id; location.access_hash = documentLocation.access_hash; location.file_reference = documentLocation.file_reference; location.thumb_size = ""; @@ -763,16 +767,31 @@ public String getFileName() { return fileName; } + public long getDocumentId() { + return documentId; + } + protected void removeStreamListener(final FileLoadOperationStream operation) { Utilities.stageQueue.postRunnable(() -> { if (streamListeners == null) { return; } - FileLog.e("FileLoadOperation " + getFileName() + " removing stream listener " + stream); + FileLog.e("FileLoadOperation " + getFileName() + " removing stream listener " + operation); streamListeners.remove(operation); + if (!isStory && streamListeners.isEmpty()) { + Utilities.stageQueue.cancelRunnable(cancelAfterNoStreamListeners); + Utilities.stageQueue.postRunnable(cancelAfterNoStreamListeners, 1200); + } else if (!streamListeners.isEmpty()) { + Utilities.stageQueue.cancelRunnable(cancelAfterNoStreamListeners); + } }); } + private final Runnable cancelAfterNoStreamListeners = () -> { + pause(); + FileLoader.getInstance(currentAccount).cancelLoadFile(getFileName()); + }; + private void copyNotLoadedRanges() { if (notLoadedBytesRanges == null) { return; @@ -788,7 +807,7 @@ public void pause() { Utilities.stageQueue.postRunnable(() -> { if (isStory) { if (BuildVars.LOGS_ENABLED) { - FileLog.d("debug_loading:" + cacheFileFinal.getName() + " pause operation, clear requests"); + FileLog.d("debug_loading: " + cacheFileFinal.getName() + " pause operation, clear requests"); } clearOperation(null, false, true); } else { @@ -860,6 +879,9 @@ public boolean start(final FileLoadOperationStream stream, final long streamOffs streamListeners.add(stream); FileLog.e("FileLoadOperation " + getFileName() + " start, adding stream " + stream); } + if (!streamListeners.isEmpty()) { + Utilities.stageQueue.cancelRunnable(cancelAfterNoStreamListeners); + } if (alreadyStarted) { if (preloadedBytesRanges != null && getDownloadedLengthFromOffsetInternal(notLoadedBytesRanges, streamStartOffset, 1) == 0) { if (preloadedBytesRanges.get(streamStartOffset) != null) { @@ -1319,62 +1341,66 @@ public void cancel() { private void cancel(boolean deleteFiles) { Utilities.stageQueue.postRunnable(() -> { - if (state != stateFinished && state != stateFailed) { - state = stateCancelling; - cancelRequests(() -> { - if (state == stateCancelling) { - onFail(false, 1); - } - }); - } - if (deleteFiles) { - if (cacheFileFinal != null) { - try { - if (!cacheFileFinal.delete()) { - cacheFileFinal.deleteOnExit(); - } - } catch (Exception e) { - FileLog.e(e); + cancelOnStage(deleteFiles); + }); + } + + private void cancelOnStage(boolean deleteFiles) { + if (state != stateFinished && state != stateFailed) { + state = stateCancelling; + cancelRequests(() -> { + if (state == stateCancelling) { + onFail(false, 1); + } + }); + } + if (deleteFiles) { + if (cacheFileFinal != null) { + try { + if (!cacheFileFinal.delete()) { + cacheFileFinal.deleteOnExit(); } + } catch (Exception e) { + FileLog.e(e); } - if (cacheFileTemp != null) { - try { - if (!cacheFileTemp.delete()) { - cacheFileTemp.deleteOnExit(); - } - } catch (Exception e) { - FileLog.e(e); + } + if (cacheFileTemp != null) { + try { + if (!cacheFileTemp.delete()) { + cacheFileTemp.deleteOnExit(); } + } catch (Exception e) { + FileLog.e(e); } - if (cacheFileParts != null) { - try { - if (!cacheFileParts.delete()) { - cacheFileParts.deleteOnExit(); - } - } catch (Exception e) { - FileLog.e(e); + } + if (cacheFileParts != null) { + try { + if (!cacheFileParts.delete()) { + cacheFileParts.deleteOnExit(); } + } catch (Exception e) { + FileLog.e(e); } - if (cacheIvTemp != null) { - try { - if (!cacheIvTemp.delete()) { - cacheIvTemp.deleteOnExit(); - } - } catch (Exception e) { - FileLog.e(e); + } + if (cacheIvTemp != null) { + try { + if (!cacheIvTemp.delete()) { + cacheIvTemp.deleteOnExit(); } + } catch (Exception e) { + FileLog.e(e); } - if (cacheFilePreload != null) { - try { - if (!cacheFilePreload.delete()) { - cacheFilePreload.deleteOnExit(); - } - } catch (Exception e) { - FileLog.e(e); + } + if (cacheFilePreload != null) { + try { + if (!cacheFilePreload.delete()) { + cacheFilePreload.deleteOnExit(); } + } catch (Exception e) { + FileLog.e(e); } } - }); + } } private void cancelRequests(Runnable fullyCancelled) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java index 4cd8c551e47..f004af5d86c 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java @@ -623,6 +623,28 @@ private void cancelLoadFile(final TLRPC.Document document, final SecureDocument } } + public void cancel(FileLoadOperation operation) { + if (operation == null) return; + final String fileName = operation.getFileName(); + LoadOperationUIObject uiObject = loadOperationPathsUI.remove(fileName); + Runnable runnable = uiObject != null ? uiObject.loadInternalRunnable : null; + boolean removed = uiObject != null; + if (runnable != null) { + fileLoaderQueue.cancelRunnable(runnable); + } + fileLoaderQueue.postRunnable(() -> { + FileLoadOperation operation2 = loadOperationPaths.remove(fileName); + if (operation2 != null) { + operation2.getQueue().cancel(operation2); + } + }); + if (removed) { + AndroidUtilities.runOnUIThread(() -> { + getNotificationCenter().postNotificationName(NotificationCenter.onDownloadingFilesChanged); + }); + } + } + public void changePriority(int priority, final TLRPC.Document document, final SecureDocument secureDocument, final WebFile webDocument, final TLRPC.FileLocation location, final String locationExt, String name) { if (location == null && document == null && webDocument == null && secureDocument == null && TextUtils.isEmpty(name)) { return; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FilePathDatabase.java b/TMessagesProj/src/main/java/org/telegram/messenger/FilePathDatabase.java index 08b809766e4..5fb6846965e 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FilePathDatabase.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FilePathDatabase.java @@ -171,12 +171,10 @@ public String getPath(long documentId, int dc, int type, boolean useQueue) { } return path; } + if (dispatchQueue != null && dispatchQueue.getHandler() != null && Thread.currentThread() == dispatchQueue.getHandler().getLooper().getThread()) { + useQueue = false; + } if (useQueue) { - if (BuildVars.DEBUG_PRIVATE_VERSION) { - if (dispatchQueue != null && dispatchQueue.getHandler() != null && Thread.currentThread() == dispatchQueue.getHandler().getLooper().getThread()) { - throw new RuntimeException("Error, lead to infinity loop"); - } - } CountDownLatch syncLatch = new CountDownLatch(1); String[] res = new String[1]; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileStreamLoadOperation.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileStreamLoadOperation.java index 8863ea054b3..cc560f0f635 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileStreamLoadOperation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileStreamLoadOperation.java @@ -52,7 +52,7 @@ public class FileStreamLoadOperation extends BaseDataSource implements FileLoadO private static final ConcurrentHashMap priorityMap = new ConcurrentHashMap<>(); public FileStreamLoadOperation() { - super(/* isNetwork= */ false); + super(/* isNetwork= */ true); } @Deprecated @@ -113,6 +113,7 @@ public long open(DataSpec dataSpec) throws IOException { file = new RandomAccessFile(currentFile, "r"); file.seek(currentOffset); if (loadOperation.isFinished()) { + super.isNetwork = false; bytesRemaining = currentFile.length() - currentOffset; if (requestedLength != C.LENGTH_UNSET) { bytesRemaining = Math.min(bytesRemaining, requestedLength - bytesTransferred); @@ -122,7 +123,8 @@ public long open(DataSpec dataSpec) throws IOException { } } } -// FileLog.e("FileStreamLoadOperation " + document.id + " open operation=" + loadOperation + " currentFile=" + currentFile + " file=" + file + " bytesRemaining=" + bytesRemaining + " me=" + this); + FileLog.e("FileStreamLoadOperation " + document.id + " open operation=" + loadOperation + " currentFile=" + currentFile + " file=" + file + " bytesRemaining=" + bytesRemaining + " me=" + this); + FileLog.e("FileStreamLoadOperation " + document.id + " " + MessageObject.getVideoWidth(document) + "x" + MessageObject.getVideoWidth(document) + " mime_type="+document.mime_type+" codec="+MessageObject.getVideoCodec(document)+" size="+ document.size); return bytesRemaining; } @@ -185,13 +187,22 @@ public int read(byte[] buffer, int offset, int readLength) throws IOException { file = new RandomAccessFile(currentFile, "r"); file.seek(currentOffset); if (loadOperation.isFinished()) { + super.isNetwork = false; bytesRemaining = currentFile.length() - currentOffset; if (requestedLength != C.LENGTH_UNSET) { bytesRemaining = Math.min(bytesRemaining, requestedLength - bytesTransferred); } } } catch (Throwable e) { - + if (loadOperation.isFinished() && !currentFile.exists()) { + FileLoader.getInstance(currentAccount).cancelLoadFile(loadOperation.getFileName()); + FileLoadOperation newLoadOperation = FileLoader.getInstance(currentAccount).loadStreamFile(this, document, null, parentObject, currentOffset, false, getCurrentPriority()); + if (this.loadOperation != newLoadOperation) { +// FileLog.e("FileStreamLoadOperation " + document.id + " read: changed operation!"); + this.loadOperation.removeStreamListener(this); + this.loadOperation = newLoadOperation; + } + } } } } else { @@ -223,7 +234,7 @@ public Uri getUri() { @Override public void close() { -// FileLog.e("FileStreamLoadOperation " + document.id + " close me=" + this); + FileLog.e("FileStreamLoadOperation " + document.id + " close me=" + this); if (loadOperation != null) { loadOperation.removeStreamListener(this); } @@ -273,15 +284,16 @@ public static Uri prepareUri(int currentAccount, TLRPC.Document document, Object return Uri.fromFile(file); } try { - String params = "?account=" + currentAccount + - "&id=" + document.id + - "&hash=" + document.access_hash + - "&dc=" + document.dc_id + - "&size=" + document.size + - "&mime=" + URLEncoder.encode(document.mime_type, "UTF-8") + - "&rid=" + FileLoader.getInstance(currentAccount).getFileReference(parent) + - "&name=" + URLEncoder.encode(FileLoader.getDocumentFileName(document), "UTF-8") + - "&reference=" + Utilities.bytesToHex(document.file_reference != null ? document.file_reference : new byte[0]); + String params = + "?account=" + currentAccount + + "&id=" + document.id + + "&hash=" + document.access_hash + + "&dc=" + document.dc_id + + "&size=" + document.size + + "&mime=" + URLEncoder.encode(document.mime_type, "UTF-8") + + "&rid=" + FileLoader.getInstance(currentAccount).getFileReference(parent) + + "&name=" + URLEncoder.encode(FileLoader.getDocumentFileName(document), "UTF-8") + + "&reference=" + Utilities.bytesToHex(document.file_reference != null ? document.file_reference : new byte[0]); return Uri.parse("tg://" + attachFileName + params); } catch (UnsupportedEncodingException e) { FileLog.e(e); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/HashtagSearchController.java b/TMessagesProj/src/main/java/org/telegram/messenger/HashtagSearchController.java index 04315b10f04..446ea747f05 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/HashtagSearchController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/HashtagSearchController.java @@ -40,8 +40,9 @@ public static HashtagSearchController getInstance(int num) { public final int currentAccount; - private final SearchResult myMessagesSearch = new SearchResult(); - private final SearchResult channelPostsSearch = new SearchResult(); + private final SearchResult myMessagesSearch; + private final SearchResult channelPostsSearch; + private final SearchResult localPostsSearch; private final SharedPreferences historyPreferences; public final ArrayList history = new ArrayList<>(); @@ -49,6 +50,9 @@ public static HashtagSearchController getInstance(int num) { private HashtagSearchController(int currentAccount) { this.currentAccount = currentAccount; + myMessagesSearch = new SearchResult(currentAccount); + channelPostsSearch = new SearchResult(currentAccount); + localPostsSearch = new SearchResult(currentAccount); historyPreferences = ApplicationLoader.applicationContext.getSharedPreferences("hashtag_search_history" + currentAccount, Activity.MODE_PRIVATE); loadHistoryFromPref(); @@ -110,11 +114,13 @@ public void removeHashtagFromHistory(String hashtag) { } @NonNull - private SearchResult getSearchResult(int searchType) { + public SearchResult getSearchResult(int searchType) { if (searchType == ChatActivity.SEARCH_MY_MESSAGES) { return myMessagesSearch; } else if (searchType == ChatActivity.SEARCH_PUBLIC_POSTS) { return channelPostsSearch; + } else if (searchType == ChatActivity.SEARCH_CHANNEL_POSTS) { + return localPostsSearch; } throw new RuntimeException("Unknown search type"); } @@ -131,25 +137,63 @@ public boolean isEndReached(int searchType) { return getSearchResult(searchType).endReached; } - public void searchHashtag(String hashtag, int guid, int searchType, int loadIndex) { + public void searchHashtag(String _query, int guid, int searchType, int loadIndex) { SearchResult search = getSearchResult(searchType); - if (search.lastHashtag == null && hashtag == null) { + if (search.lastHashtag == null && _query == null) { return; } - if (hashtag != null && hashtag.isEmpty()) { + if (_query != null && _query.isEmpty()) { return; } - if (hashtag == null) { - hashtag = search.lastHashtag; - } else if (!TextUtils.equals(hashtag, search.lastHashtag)) { + if (_query == null) { + _query = search.lastHashtag; + } else if (!TextUtils.equals(_query, search.lastHashtag)) { search.clear(); + } else if (search.loading) { + return; + } + search.lastHashtag = _query; + final String query = _query; + + String _username = null; + int atIndex = _query.indexOf('@'); + if (atIndex >= 0) { + _username = _query.substring(atIndex + 1); + _query = _query.substring(0, atIndex); + } + final String hashtag = _query; + final String username = _username; + search.loading = true; + + final int[] reqId = new int[1]; + TLObject chat = null; + if (!TextUtils.isEmpty(username)) { + chat = MessagesController.getInstance(currentAccount).getUserOrChat(username); + if (chat == null) { + reqId[0] = search.reqId = MessagesController.getInstance(currentAccount).getUserNameResolver().resolve(username, resolvedChatId -> { + if (!TextUtils.equals(search.lastHashtag, query)) return; + final TLObject resolvedChat = MessagesController.getInstance(currentAccount).getUserOrChat(username); + if (resolvedChat == null) { + if (reqId[0] == search.reqId) { + search.reqId = -1; + } else { + return; + } + search.loading = false; + search.endReached = true; + search.count = 0; + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.hashtagSearchUpdated, guid, search.count, search.endReached, search.getMask(), search.selectedIndex, 0); + return; + } + searchHashtag(query, guid, searchType, loadIndex); + }); + return; + } } - search.lastHashtag = hashtag; - final String query = hashtag; - int limit = 30; + int limit = 21; TLObject request; if (searchType == ChatActivity.SEARCH_MY_MESSAGES) { TLRPC.TL_messages_searchGlobal req = new TLRPC.TL_messages_searchGlobal(); @@ -164,18 +208,30 @@ public void searchHashtag(String hashtag, int guid, int searchType, int loadInde } request = req; } else { - TLRPC.TL_channels_searchPosts req = new TLRPC.TL_channels_searchPosts(); - req.limit = limit; - req.hashtag = query; - req.offset_peer = new TLRPC.TL_inputPeerEmpty(); - if (search.lastOffsetPeer != null) { - req.offset_rate = search.lastOffsetRate; - req.offset_id = search.lastOffsetId; - req.offset_peer = MessagesController.getInstance(currentAccount).getInputPeer(search.lastOffsetPeer); + if (chat != null) { + TLRPC.TL_messages_search req = new TLRPC.TL_messages_search(); + req.filter = new TLRPC.TL_inputMessagesFilterEmpty(); + req.peer = MessagesController.getInputPeer(chat); + req.q = hashtag; + req.limit = limit; + if (search.lastOffsetId != 0) { + req.offset_id = search.lastOffsetId; + } + request = req; + } else { + TLRPC.TL_channels_searchPosts req = new TLRPC.TL_channels_searchPosts(); + req.limit = limit; + req.hashtag = query; + req.offset_peer = new TLRPC.TL_inputPeerEmpty(); + if (search.lastOffsetPeer != null) { + req.offset_rate = search.lastOffsetRate; + req.offset_id = search.lastOffsetId; + req.offset_peer = MessagesController.getInstance(currentAccount).getInputPeer(search.lastOffsetPeer); + } + request = req; } - request = req; } - ConnectionsManager.getInstance(currentAccount).sendRequest(request, (res, err) -> { + reqId[0] = search.reqId = ConnectionsManager.getInstance(currentAccount).sendRequest(request, (res, err) -> { if (res instanceof TLRPC.messages_Messages) { TLRPC.messages_Messages messages = (TLRPC.messages_Messages) res; ArrayList messageObjects = new ArrayList<>(); @@ -184,11 +240,17 @@ public void searchHashtag(String hashtag, int guid, int searchType, int loadInde if (obj.hasValidGroupId()) { obj.isPrimaryGroupMessage = true; } - obj.setQuery(query); + obj.setQuery(query, false); messageObjects.add(obj); } AndroidUtilities.runOnUIThread(() -> { + if (reqId[0] == search.reqId) { + search.reqId = -1; + } else { + return; + } + search.loading = false; search.lastOffsetRate = messages.next_rate; for (MessageObject msg : messageObjects) { @@ -205,7 +267,7 @@ public void searchHashtag(String hashtag, int guid, int searchType, int loadInde if (!messages.messages.isEmpty()) { TLRPC.Message lastMsg = messages.messages.get(messages.messages.size() - 1); - search.lastOffsetId = lastMsg.id; + search.lastOffsetId = lastMsg.realId; search.lastOffsetPeer = lastMsg.peer_id; } @@ -268,18 +330,25 @@ public int hashCode() { } } - private static class SearchResult { - ArrayList messages = new ArrayList<>(); - HashMap generatedIds = new HashMap<>(); + public static class SearchResult { + public final ArrayList messages = new ArrayList<>(); + public final HashMap generatedIds = new HashMap<>(); - int lastOffsetRate; - int lastOffsetId; - TLRPC.Peer lastOffsetPeer; - int lastGeneratedId = Integer.MAX_VALUE; - String lastHashtag; - int selectedIndex; - int count; - boolean endReached; + private final int currentAccount; + public SearchResult(int account) { + this.currentAccount = account; + } + + public int reqId = -1; + public boolean loading; + public int lastOffsetRate; + public int lastOffsetId; + public TLRPC.Peer lastOffsetPeer; + public int lastGeneratedId = Integer.MAX_VALUE; + public String lastHashtag; + public int selectedIndex; + public int count; + public boolean endReached; int getMask() { int mask = 0; @@ -293,6 +362,10 @@ int getMask() { } void clear() { + if (reqId >= 0) { + ConnectionsManager.getInstance(currentAccount).cancelRequest(reqId, true); + reqId = -1; + } messages.clear(); generatedIds.clear(); lastOffsetRate = 0; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ImageReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/ImageReceiver.java index ce5c6b1b1e1..e0d06542563 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ImageReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ImageReceiver.java @@ -222,6 +222,7 @@ private void clear() { private int currentAccount; private View parentView; + private Runnable parentRunnable; private int param; private Object currentParentObject; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java b/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java index 3b9291eb330..3228ee6f9e0 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java @@ -2095,6 +2095,56 @@ public static String formatPmSeenDate(long date) { return "LOC_ERR"; } + public static String formatPmEditedDate(long date) { + try { + date *= 1000; + Calendar rightNow = Calendar.getInstance(); + int day = rightNow.get(Calendar.DAY_OF_YEAR); + int year = rightNow.get(Calendar.YEAR); + rightNow.setTimeInMillis(date); + int dateDay = rightNow.get(Calendar.DAY_OF_YEAR); + int dateYear = rightNow.get(Calendar.YEAR); + + if (dateDay == day && year == dateYear) { + return LocaleController.formatString(R.string.PmEditedTodayAt, getInstance().getFormatterDay().format(new Date(date))); + } else if (dateDay + 1 == day && year == dateYear) { + return LocaleController.formatString(R.string.PmEditedYesterdayAt, getInstance().getFormatterDay().format(new Date(date))); + } else if (Math.abs(System.currentTimeMillis() - date) < 31536000000L) { + return LocaleController.formatString(R.string.PmEditedDateTimeAt, getInstance().getFormatterDayMonth().format(new Date(date)), getInstance().getFormatterDay().format(new Date(date))); + } else { + return LocaleController.formatString(R.string.PmEditedDateTimeAt, getInstance().getFormatterYear().format(new Date(date)), getInstance().getFormatterDay().format(new Date(date))); + } + } catch (Exception e) { + FileLog.e(e); + } + return "LOC_ERR"; + } + + public static String formatPmFwdDate(long date) { + try { + date *= 1000; + Calendar rightNow = Calendar.getInstance(); + int day = rightNow.get(Calendar.DAY_OF_YEAR); + int year = rightNow.get(Calendar.YEAR); + rightNow.setTimeInMillis(date); + int dateDay = rightNow.get(Calendar.DAY_OF_YEAR); + int dateYear = rightNow.get(Calendar.YEAR); + + if (dateDay == day && year == dateYear) { + return LocaleController.formatString(R.string.PmFwdOriginalTodayAt, getInstance().getFormatterDay().format(new Date(date))); + } else if (dateDay + 1 == day && year == dateYear) { + return LocaleController.formatString(R.string.PmFwdOriginalYesterdayAt, getInstance().getFormatterDay().format(new Date(date))); + } else if (Math.abs(System.currentTimeMillis() - date) < 31536000000L) { + return LocaleController.formatString(R.string.PmFwdOriginalDateTimeAt, getInstance().getFormatterDayMonth().format(new Date(date)), getInstance().getFormatterDay().format(new Date(date))); + } else { + return LocaleController.formatString(R.string.PmFwdOriginalDateTimeAt, getInstance().getFormatterYear().format(new Date(date)), getInstance().getFormatterDay().format(new Date(date))); + } + } catch (Exception e) { + FileLog.e(e); + } + return "LOC_ERR"; + } + public static String formatShortDate(long date) { try { date *= 1000; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java index bfffef1cb7d..62420c6b46d 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java @@ -2968,7 +2968,7 @@ private void updateVideoState(MessageObject messageObject, int[] playCount, bool } } } else if (videoPlayer.isPlaying() && playbackState == ExoPlayer.STATE_ENDED) { - if (playingMessageObject.isVideo() && !destroyAtEnd && (playCount == null || playCount[0] < 4)) { + if (playingMessageObject != null && playingMessageObject.isVideo() && !destroyAtEnd && (playCount == null || playCount[0] < 4)) { videoPlayer.seekTo(0); if (playCount != null) { playCount[0]++; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java index 6884ddd916c..5e2b9261403 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java @@ -9,6 +9,7 @@ package org.telegram.messenger; import android.app.Activity; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -1279,6 +1280,32 @@ public void setPlaceholderImage(BackupImageView imageView, String setName, Strin }); } + public static String inputSetKey(TLRPC.InputStickerSet i) { + if (i instanceof TLRPC.TL_inputStickerSetID) + return "id" + i.id + "access_hash" + i.access_hash; + if (i instanceof TLRPC.TL_inputStickerSetShortName) + return "short" + i.short_name; + if (i instanceof TLRPC.TL_inputStickerSetEmpty) + return "empty"; + if (i instanceof TLRPC.TL_inputStickerSetAnimatedEmoji) + return "animatedEmoji"; + if (i instanceof TLRPC.TL_inputStickerSetEmojiGenericAnimations) + return "emojiGenericAnimations"; + if (i instanceof TLRPC.TL_inputStickerSetEmojiChannelDefaultStatuses) + return "emojiChannelDefaultStatuses"; + if (i instanceof TLRPC.TL_inputStickerSetDice) + return "dice" + ((TLRPC.TL_inputStickerSetDice) i).emoticon; + if (i instanceof TLRPC.TL_inputStickerSetPremiumGifts) + return "premiumGifts"; + if (i instanceof TLRPC.TL_inputStickerSetEmojiDefaultTopicIcons) + return "defaultTopicIcons"; + if (i instanceof TLRPC.TL_inputStickerSetEmojiDefaultStatuses) + return "emojiDefaultStatuses"; + return "null"; + } + + private final HashSet loadingStickerSetsKeys = new HashSet<>(); + public TLRPC.TL_messages_stickerSet getStickerSet(TLRPC.InputStickerSet inputStickerSet, boolean cacheOnly) { return getStickerSet(inputStickerSet, null, cacheOnly, null); } @@ -1311,11 +1338,15 @@ public TLRPC.TL_messages_stickerSet getStickerSet(TLRPC.InputStickerSet inputSti } return cacheSet; } + final String key = inputSetKey(inputStickerSet); + if (onResponse == null && loadingStickerSetsKeys.contains(key)) return null; + loadingStickerSetsKeys.add(key); if (inputStickerSet instanceof TLRPC.TL_inputStickerSetID) { getMessagesStorage().getStorageQueue().postRunnable(() -> { TLRPC.TL_messages_stickerSet cachedSet = getCachedStickerSetInternal(inputStickerSet.id, hash); AndroidUtilities.runOnUIThread(() -> { if (cachedSet != null) { + loadingStickerSetsKeys.remove(key); if (onResponse != null) { onResponse.run(cachedSet); } @@ -1324,8 +1355,9 @@ public TLRPC.TL_messages_stickerSet getStickerSet(TLRPC.InputStickerSet inputSti stickerSetsByName.put(cachedSet.set.short_name.toLowerCase(), cachedSet); } getNotificationCenter().postNotificationName(NotificationCenter.groupStickersDidLoad, cachedSet.set.id, cachedSet); - } else { + } else if (!cacheOnly) { fetchStickerSetInternal(inputStickerSet, (ok, set) -> { + loadingStickerSetsKeys.remove(key); if (onResponse != null) { onResponse.run(set); } @@ -1336,6 +1368,8 @@ public TLRPC.TL_messages_stickerSet getStickerSet(TLRPC.InputStickerSet inputSti getNotificationCenter().postNotificationName(NotificationCenter.groupStickersDidLoad, set.set.id, set); } }); + } else { + loadingStickerSetsKeys.remove(key); } }); }); @@ -1344,6 +1378,7 @@ public TLRPC.TL_messages_stickerSet getStickerSet(TLRPC.InputStickerSet inputSti TLRPC.TL_messages_stickerSet cachedSet = getCachedStickerSetInternal(inputStickerSet.short_name.toLowerCase(), hash); AndroidUtilities.runOnUIThread(() -> { if (cachedSet != null) { + loadingStickerSetsKeys.remove(key); if (onResponse != null) { onResponse.run(cachedSet); } @@ -1352,8 +1387,9 @@ public TLRPC.TL_messages_stickerSet getStickerSet(TLRPC.InputStickerSet inputSti stickerSetsByName.put(cachedSet.set.short_name.toLowerCase(), cachedSet); } getNotificationCenter().postNotificationName(NotificationCenter.groupStickersDidLoad, cachedSet.set.id, cachedSet); - } else { + } else if (!cacheOnly) { fetchStickerSetInternal(inputStickerSet, (ok, set) -> { + loadingStickerSetsKeys.remove(key); if (onResponse != null) { onResponse.run(set); } @@ -1364,11 +1400,14 @@ public TLRPC.TL_messages_stickerSet getStickerSet(TLRPC.InputStickerSet inputSti getNotificationCenter().postNotificationName(NotificationCenter.groupStickersDidLoad, set.set.id, set); } }); + } else { + loadingStickerSetsKeys.remove(key); } }); }); } else if (!cacheOnly) { fetchStickerSetInternal(inputStickerSet, (ok, set) -> { + loadingStickerSetsKeys.remove(key); if (onResponse != null) { onResponse.run(set); } @@ -1387,6 +1426,8 @@ public TLRPC.TL_messages_stickerSet getStickerSet(TLRPC.InputStickerSet inputSti getNotificationCenter().postNotificationName(NotificationCenter.groupStickersDidLoad, set.set.id, set); } }); + } else { + loadingStickerSetsKeys.remove(key); } return null; } @@ -1527,26 +1568,27 @@ private TLRPC.TL_messages_stickerSet getCachedStickerSetInternal(String short_na return set; } - private final HashMap>> loadingStickerSets = new HashMap<>(); + private final HashMap>> loadingStickerSets = new HashMap<>(); private void fetchStickerSetInternal(TLRPC.InputStickerSet inputStickerSet, Utilities.Callback2 onDone) { if (onDone == null) { return; } - ArrayList> loading = loadingStickerSets.get(inputStickerSet); + final String key = inputSetKey(inputStickerSet); + ArrayList> loading = loadingStickerSets.get(key); if (loading != null && loading.size() > 0) { loading.add(onDone); return; } if (loading == null) { - loadingStickerSets.put(inputStickerSet, loading = new ArrayList<>()); + loadingStickerSets.put(key, loading = new ArrayList<>()); } loading.add(onDone); TLRPC.TL_messages_getStickerSet req = new TLRPC.TL_messages_getStickerSet(); req.stickerset = inputStickerSet; getConnectionsManager().sendRequest(req, (response, error) -> { AndroidUtilities.runOnUIThread(() -> { - ArrayList> loadingCallbacks = loadingStickerSets.get(inputStickerSet); + ArrayList> loadingCallbacks = loadingStickerSets.get(key); if (loadingCallbacks != null) { for (int i = 0; i < loadingCallbacks.size(); ++i) { if (response != null) { @@ -1556,7 +1598,7 @@ private void fetchStickerSetInternal(TLRPC.InputStickerSet inputStickerSet, Util } } } - loadingStickerSets.remove(inputStickerSet); + loadingStickerSets.remove(key); }); }); } @@ -3786,6 +3828,7 @@ public void searchMessagesInChat(String query, long dialogId, long mergeDialogId searchServerResultMessagesMap[1].clear(); getNotificationCenter().postNotificationName(NotificationCenter.chatSearchResultsLoading, guid); } + final boolean isHashtag = query != null && (query.trim().startsWith("#") || query.trim().startsWith("$")); if (messagesSearchEndReached[0] && !messagesSearchEndReached[1] && mergeDialogId != 0) { queryWithDialog = mergeDialogId; } @@ -3921,7 +3964,7 @@ public void searchMessagesInChat(String query, long dialogId, long mergeDialogId if (messageObject.hasValidGroupId()) { messageObject.isPrimaryGroupMessage = true; } - messageObject.setQuery(finalQuery); + messageObject.setQuery(finalQuery, !isHashtag); messageObjects.add(messageObject); } } @@ -5452,6 +5495,7 @@ private void deletePeer(long dialogId, int type) { public static int SHORTCUT_TYPE_USER_OR_CHAT = 0; public static int SHORTCUT_TYPE_ATTACHED_BOT = 1; + private Intent createIntrnalShortcutIntent(long dialogId) { Intent shortcutIntent = new Intent(ApplicationLoader.applicationContext, OpenChatReceiver.class); @@ -5491,10 +5535,18 @@ private Intent createIntrnalAttachedBotShortcutIntent(long botId) { return shortcutIntent; } + public final HashMap> shortcutCallbacks = new HashMap<>(); + public void installShortcut(long dialogId, int type) { + installShortcut(dialogId, type, null); + } + public void installShortcut(long dialogId, int type, Utilities.Callback callback) { try { Intent shortcutIntent = type == SHORTCUT_TYPE_USER_OR_CHAT ? createIntrnalShortcutIntent(dialogId) : createIntrnalAttachedBotShortcutIntent(dialogId); if (shortcutIntent == null) { + if (callback != null) { + callback.run(false); + } return; } TLRPC.User user = null; @@ -5503,6 +5555,9 @@ public void installShortcut(long dialogId, int type) { int encryptedChatId = DialogObject.getEncryptedChatId(dialogId); TLRPC.EncryptedChat encryptedChat = getMessagesController().getEncryptedChat(encryptedChatId); if (encryptedChat == null) { + if (callback != null) { + callback.run(false); + } return; } user = getMessagesController().getUser(encryptedChat.user_id); @@ -5511,9 +5566,15 @@ public void installShortcut(long dialogId, int type) { } else if (DialogObject.isChatDialog(dialogId)) { chat = getMessagesController().getChat(-dialogId); } else { + if (callback != null) { + callback.run(false); + } return; } if (user == null && chat == null) { + if (callback != null) { + callback.run(false); + } return; } @@ -5602,9 +5663,9 @@ public void installShortcut(long dialogId, int type) { if (Build.VERSION.SDK_INT >= 26) { String idPrefix = type == SHORTCUT_TYPE_USER_OR_CHAT ? "sdid_" : "bdid_"; ShortcutInfoCompat.Builder pinShortcutInfo = - new ShortcutInfoCompat.Builder(ApplicationLoader.applicationContext, idPrefix + dialogId) - .setShortLabel(name) - .setIntent(shortcutIntent); + new ShortcutInfoCompat.Builder(ApplicationLoader.applicationContext, idPrefix + dialogId) + .setShortLabel(name) + .setIntent(shortcutIntent); if (bitmap != null) { pinShortcutInfo.setIcon(IconCompat.createWithBitmap(bitmap)); @@ -5624,7 +5685,21 @@ public void installShortcut(long dialogId, int type) { } } - ShortcutManagerCompat.requestPinShortcut(ApplicationLoader.applicationContext, pinShortcutInfo.build(), null); + PendingIntent callbackIntent = null; + if (callback != null) { + byte[] bytes = new byte[16]; + Utilities.fastRandom.nextBytes(bytes); + final String req_id = Utilities.bytesToHex(bytes); + + final Intent intent = new Intent(ApplicationLoader.applicationContext, ShortcutResultReceiver.class); + intent.putExtra("account", currentAccount); + intent.putExtra("req_id", req_id); + callbackIntent = PendingIntent.getBroadcast(ApplicationLoader.applicationContext, 0, intent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); + + shortcutCallbacks.put(req_id, callback); + } + + ShortcutManagerCompat.requestPinShortcut(ApplicationLoader.applicationContext, pinShortcutInfo.build(), callbackIntent == null ? null : callbackIntent.getIntentSender()); } else { Intent addIntent = new Intent(); if (bitmap != null) { @@ -5736,12 +5811,7 @@ public boolean isShortcutAdded(long dialogId, int type) { } public boolean canCreateAttachedMenuBotShortcut(long botId) { - for (int i = 0; i < attachMenuBots.bots.size(); i++) { - if (attachMenuBots.bots.get(i).bot_id == botId) { - return attachMenuBots.bots.get(i).show_in_side_menu && !isShortcutAdded(botId, MediaDataController.SHORTCUT_TYPE_ATTACHED_BOT); - } - } - return false; + return true; } //---------------- SEARCH END ---------------- @@ -7668,10 +7738,14 @@ public void saveDraft(long dialogId, long threadId, TLRPC.DraftMessage draft, TL } threads.put(threadId, replyToMessage); - SerializedData serializedData = new SerializedData(replyToMessage.getObjectSize()); - replyToMessage.serializeToStream(serializedData); - editor.putString(threadId == 0 ? ("r_" + dialogId) : ("rt_" + dialogId + "_" + threadId), Utilities.bytesToHex(serializedData.toByteArray())); - serializedData.cleanup(); + try { + SerializedData serializedData = new SerializedData(replyToMessage.getObjectSize()); + replyToMessage.serializeToStream(serializedData); + editor.putString(threadId == 0 ? ("r_" + dialogId) : ("rt_" + dialogId + "_" + threadId), Utilities.bytesToHex(serializedData.toByteArray())); + serializedData.cleanup(); + } catch (Exception e) { + FileLog.e(e); + } } editor.commit(); if (fromServer && (threadId == 0 || getMessagesController().isForum(dialogId))) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessageCustomParamsHelper.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessageCustomParamsHelper.java index 8077a925f1b..18e774033e7 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessageCustomParamsHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessageCustomParamsHelper.java @@ -73,12 +73,12 @@ private static class Params_v1 extends TLObject { private Params_v1(TLRPC.Message message) { this.message = message; - flags += message.voiceTranscription != null ? 1 : 0; - flags += message.voiceTranscriptionForce ? 2 : 0; + flags |= message.voiceTranscription != null ? 1 : 0; + flags |= message.voiceTranscriptionForce ? 2 : 0; - flags += message.originalLanguage != null ? 4 : 0; - flags += message.translatedToLanguage != null ? 8 : 0; - flags += message.translatedText != null ? 16 : 0; + flags |= message.originalLanguage != null ? 4 : 0; + flags |= message.translatedToLanguage != null ? 8 : 0; + flags |= message.translatedText != null ? 16 : 0; } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java index d5dccd3912f..b820249fd50 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java @@ -196,6 +196,7 @@ public class MessageObject { public int audioPlayerDuration; public double attributeDuration; public boolean isDateObject; + public boolean isVideoConversionObject; public TLObject photoThumbsObject; public TLObject photoThumbsObject2; public ArrayList photoThumbs; @@ -310,6 +311,7 @@ public int getChatMode() { public CharSequence vCardData; public ArrayList highlightedWords; + public boolean messageTrimmedToHighlightCut = true; public CharSequence messageTrimmedToHighlight; public int parentWidth; @@ -476,6 +478,45 @@ public static double getDocumentDuration(TLRPC.Document document) { return 0; } + public static int getVideoWidth(TLRPC.Document document) { + if (document == null) { + return 0; + } + for (int a = 0, size = document.attributes.size(); a < size; a++) { + TLRPC.DocumentAttribute attribute = document.attributes.get(a); + if (attribute instanceof TLRPC.TL_documentAttributeVideo) { + return attribute.w; + } + } + return 0; + } + + public static int getVideoHeight(TLRPC.Document document) { + if (document == null) { + return 0; + } + for (int a = 0, size = document.attributes.size(); a < size; a++) { + TLRPC.DocumentAttribute attribute = document.attributes.get(a); + if (attribute instanceof TLRPC.TL_documentAttributeVideo) { + return attribute.h; + } + } + return 0; + } + + public static String getVideoCodec(TLRPC.Document document) { + if (document == null) { + return null; + } + for (int a = 0, size = document.attributes.size(); a < size; a++) { + TLRPC.DocumentAttribute attribute = document.attributes.get(a); + if (attribute instanceof TLRPC.TL_documentAttributeVideo) { + return ((TLRPC.TL_documentAttributeVideo) attribute).video_codec; + } + } + return null; + } + public boolean isWallpaperAction() { return type == TYPE_ACTION_WALLPAPER || (messageOwner != null && messageOwner.action instanceof TLRPC.TL_messageActionSetSameChatWallPaper); } @@ -2624,19 +2665,30 @@ public MessageObject(int accountNum, TLRPC.TL_channelAdminLogEvent event, ArrayL if (getMedia(newMessage) != null && !(getMedia(newMessage) instanceof TLRPC.TL_messageMediaEmpty) && !(getMedia(newMessage) instanceof TLRPC.TL_messageMediaWebPage)/* && TextUtils.isEmpty(newMessage.message)*/) { boolean changedCaption; boolean changedMedia; + boolean addedMedia = false; if (!TextUtils.equals(newMessage.message, oldMessage.message)) { changedCaption = true; } else { changedCaption = false; } - if (getMedia(newMessage).getClass() != oldMessage.media.getClass() || - getMedia(newMessage).photo != null && oldMessage.media.photo != null && getMedia(newMessage).photo.id != oldMessage.media.photo.id || - getMedia(newMessage).document != null && oldMessage.media.document != null && getMedia(newMessage).document.id != oldMessage.media.document.id) { + TLRPC.MessageMedia newMedia = getMedia(newMessage); + TLRPC.MessageMedia oldMedia = getMedia(oldMessage); + if (oldMedia == null) { + addedMedia = true; + changedMedia = false; + } else if ( + newMedia.getClass() != oldMedia.getClass() || + newMedia.photo != null && oldMedia.photo != null && newMedia.photo.id != oldMedia.photo.id || + newMedia.document != null && oldMedia.document != null && getMedia(newMessage).document.id != oldMedia.document.id) { + addedMedia = false; changedMedia = true; } else { + addedMedia = false; changedMedia = false; } - if (changedMedia && changedCaption) { + if (addedMedia) { + messageText = replaceWithLink(getString(R.string.EventLogAddedMedia), "un1", fromUser); + } else if (changedMedia && changedCaption) { messageText = replaceWithLink(getString(R.string.EventLogEditedMediaCaption), "un1", fromUser); } else if (changedCaption) { messageText = replaceWithLink(getString(R.string.EventLogEditedCaption), "un1", fromUser); @@ -5843,6 +5895,9 @@ public String getExtension() { } public String getFileName() { + if (getDocument() != null) { + return getFileName(getDocument()); + } return getFileName(messageOwner); } @@ -6160,7 +6215,7 @@ public static void addUrlsByPattern(boolean isOut, CharSequence charSequence, bo matcher = instagramUrlPattern.matcher(charSequence); } else { if (urlPattern == null) { - urlPattern = Pattern.compile("(^|\\s)/[a-zA-Z@\\d_]{1,255}|(^|\\s|\\()@[a-zA-Z\\d_]{1,32}|(^|\\s|\\()#[^0-9][\\w.]+|(^|\\s)\\$[A-Z]{3,8}([ ,.]|$)"); + urlPattern = Pattern.compile("(^|\\s)/[a-zA-Z@\\d_]{1,255}|(^|\\s|\\()@[a-zA-Z\\d_]{1,32}|(^|\\s|\\()#[^0-9][\\w.]+(@[^0-9][\\w.]+)?|(^|\\s|\\()\\$[^0-9][\\w.]+(@[^0-9][\\w.]+)?|(^|\\s)\\$[A-Z]{3,8}([ ,.]|$)"); } matcher = urlPattern.matcher(charSequence); } @@ -6314,7 +6369,7 @@ public static int getInlineResultDuration(TLRPC.BotInlineResult inlineResult) { // only set in searching with tags public boolean isPrimaryGroupMessage; public boolean hasValidGroupId() { - return getGroupId() != 0 && (photoThumbs != null && !photoThumbs.isEmpty() || sendPreview && (type == TYPE_VIDEO || type == TYPE_PHOTO) || isMusic() || isDocument()); + return getGroupId() != 0 && (photoThumbs != null && !photoThumbs.isEmpty() || type == TYPE_VIDEO || type == TYPE_PHOTO || isMusic() || isDocument()); } public long getGroupIdForUse() { @@ -7868,7 +7923,7 @@ public boolean isOutOwner() { return isOutOwnerCached; } long selfUserId = UserConfig.getInstance(currentAccount).getClientUserId(); - if ((isSaved || getDialogId() == selfUserId)) { + if (isSaved || getDialogId() == selfUserId) { if (messageOwner.fwd_from != null) { return isOutOwnerCached = messageOwner.fwd_from.from_id != null && messageOwner.fwd_from.from_id.user_id == selfUserId || messageOwner.fwd_from.saved_out; } else { @@ -7882,10 +7937,6 @@ public boolean isOutOwner() { if (messageOwner.fwd_from == null) { return isOutOwnerCached = true; } - if (getDialogId() == selfUserId) { - return isOutOwnerCached = messageOwner.fwd_from.from_id instanceof TLRPC.TL_peerUser && messageOwner.fwd_from.from_id.user_id == selfUserId && (messageOwner.fwd_from.saved_from_peer == null || messageOwner.fwd_from.saved_from_peer.user_id == selfUserId) - || messageOwner.fwd_from.saved_from_peer != null && messageOwner.fwd_from.saved_from_peer.user_id == selfUserId && (messageOwner.fwd_from.from_id == null || messageOwner.fwd_from.from_id.user_id == selfUserId); - } return isOutOwnerCached = messageOwner.fwd_from.saved_from_peer == null || messageOwner.fwd_from.saved_from_peer.user_id == selfUserId; } @@ -8136,6 +8187,7 @@ public boolean isFromChannel() { } public boolean isFromGroup() { + if (messageOwner == null) return false; TLRPC.Chat chat = messageOwner.peer_id != null && messageOwner.peer_id.channel_id != 0 ? getChat(null, null, messageOwner.peer_id.channel_id) : null; return messageOwner.from_id instanceof TLRPC.TL_peerChannel && ChatObject.isChannel(chat) && chat.megagroup; } @@ -8145,7 +8197,11 @@ public boolean isForwardedChannelPost() { } public boolean isUnread() { - return messageOwner.unread; + return messageOwner != null && messageOwner.unread; + } + + public boolean isEdited() { + return messageOwner != null && (messageOwner.flags & TLRPC.MESSAGE_FLAG_EDITED) != 0 && messageOwner.edit_date != 0 && !messageOwner.edit_hide; } public boolean isContentUnread() { @@ -8156,10 +8212,6 @@ public void setIsRead() { messageOwner.unread = false; } - public int getUnradFlags() { - return getUnreadFlags(messageOwner); - } - public static int getUnreadFlags(TLRPC.Message message) { int flags = 0; if (!message.unread) { @@ -8206,6 +8258,11 @@ public static long getMediaSize(TLRPC.MessageMedia media) { } public long getSize() { + if (highestQuality != null) { + return highestQuality.document.size; + } else if (thumbQuality != null) { + return thumbQuality.document.size; + } return getMessageSize(messageOwner); } @@ -8338,6 +8395,7 @@ public long getDialogId() { } public boolean canStreamVideo() { + if (hasVideoQualities()) return true; TLRPC.Document document = getDocument(); if (document == null || document instanceof TLRPC.TL_documentEncrypted) { return false; @@ -8442,7 +8500,7 @@ public boolean isEditingMedia() { } public boolean isSendError() { - return messageOwner.send_state == MESSAGE_SEND_STATE_SEND_ERROR && messageOwner.id < 0 || scheduled && messageOwner.id > 0 && messageOwner.date < ConnectionsManager.getInstance(currentAccount).getCurrentTime() - 60; + return messageOwner.send_state == MESSAGE_SEND_STATE_SEND_ERROR && messageOwner.id < 0 || scheduled && messageOwner.id > 0 && messageOwner.date < ConnectionsManager.getInstance(currentAccount).getCurrentTime() - (messageOwner.video_processing_pending ? 5 * 60 : 60); } public boolean isSent() { @@ -8677,6 +8735,9 @@ public TLRPC.Document getDocument() { if (emojiAnimatedSticker != null) { return emojiAnimatedSticker; } + if (hasVideoQualities() && highestQuality != null) { + return highestQuality.document; + } return getDocument(messageOwner); } @@ -9619,6 +9680,8 @@ public boolean canEditMedia() { return true; } else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaDocument) { return !isVoice() && !isSticker() && !isAnimatedSticker() && !isRoundVideo(); + } else if (isMediaEmpty()) { + return true; } return false; } @@ -9651,6 +9714,7 @@ public static boolean canEditMessageAnytime(int currentAccount, TLRPC.Message me } public static boolean canEditMessageScheduleTime(int currentAccount, TLRPC.Message message, TLRPC.Chat chat) { + if (message.video_processing_pending) return false; if (chat == null && message.peer_id.channel_id != 0) { chat = MessagesController.getInstance(currentAccount).getChat(message.peer_id.channel_id); if (chat == null) { @@ -9966,37 +10030,42 @@ public void checkMediaExistance(boolean useFileDatabaseQueue) { mediaExists = FileLoader.getInstance(currentAccount).getPathToAttach(photo.video_sizes.get(0), null, true, useFileDatabaseQueue).exists(); } } + updateQualitiesCached(useFileDatabaseQueue); } public void setQuery(String query) { + setQuery(query, true); + } + public void setQuery(String query, boolean cut) { if (TextUtils.isEmpty(query)) { highlightedWords = null; messageTrimmedToHighlight = null; + messageTrimmedToHighlightCut = true; return; } ArrayList foundWords = new ArrayList<>(); query = query.trim().toLowerCase(); - String[] queryWord = query.split("\\P{L}+"); + String[] queryWord = query.split("[^\\p{L}#$]+"); ArrayList searchForWords = new ArrayList<>(); if (messageOwner.reply_to != null && !TextUtils.isEmpty(messageOwner.reply_to.quote_text)) { String message = messageOwner.reply_to.quote_text.trim().toLowerCase(); if (message.contains(query) && !foundWords.contains(query)) { foundWords.add(query); - handleFoundWords(foundWords, queryWord, true); + handleFoundWords(foundWords, queryWord, true, cut); return; } - String[] words = message.split("\\P{L}+"); + String[] words = message.split("[^\\p{L}#$]+"); searchForWords.addAll(Arrays.asList(words)); } if (!TextUtils.isEmpty(messageOwner.message)) { String message = messageOwner.message.trim().toLowerCase(); if (message.contains(query) && !foundWords.contains(query)) { foundWords.add(query); - handleFoundWords(foundWords, queryWord, false); + handleFoundWords(foundWords, queryWord, false, cut); return; } - String[] words = message.split("\\P{L}+"); + String[] words = message.split("[^\\p{L}#$]+"); searchForWords.addAll(Arrays.asList(words)); } if (getDocument() != null) { @@ -10004,7 +10073,7 @@ public void setQuery(String query) { if (fileName.contains(query) && !foundWords.contains(query)) { foundWords.add(query); } - String[] words = fileName.split("\\P{L}+"); + String[] words = fileName.split("[^\\p{L}#$]+"); searchForWords.addAll(Arrays.asList(words)); } @@ -10019,7 +10088,7 @@ public void setQuery(String query) { if (title.contains(query) && !foundWords.contains(query)) { foundWords.add(query); } - String[] words = title.split("\\P{L}+"); + String[] words = title.split("[^\\p{L}#$]+"); searchForWords.addAll(Arrays.asList(words)); } } @@ -10030,7 +10099,7 @@ public void setQuery(String query) { if (musicAuthor.contains(query) && !foundWords.contains(query)) { foundWords.add(query); } - String[] words = musicAuthor.split("\\P{L}+"); + String[] words = musicAuthor.split("[^\\p{L}#$]+"); searchForWords.addAll(Arrays.asList(words)); } for (int k = 0; k < queryWord.length; k++) { @@ -10065,10 +10134,13 @@ public void setQuery(String query) { } } } - handleFoundWords(foundWords, queryWord, false); + handleFoundWords(foundWords, queryWord, false, cut); } private void handleFoundWords(ArrayList foundWords, String[] queryWord, boolean inQuote) { + handleFoundWords(foundWords, queryWord, inQuote, true); + } + private void handleFoundWords(ArrayList foundWords, String[] queryWord, boolean inQuote, boolean cut) { if (!foundWords.isEmpty()) { boolean foundExactly = false; for (int i = 0; i < foundWords.size(); i++) { @@ -10128,11 +10200,12 @@ private void handleFoundWords(ArrayList foundWords, String[] queryWord, if (startHighlightedIndex < 0) { startHighlightedIndex = 0; } - if (lastIndex > maxSymbols) { + if (lastIndex > maxSymbols && cut) { int newStart = Math.max(0, startHighlightedIndex - (int) (maxSymbols * .1f)); charSequence = charSequence.subSequence(newStart, Math.min(lastIndex, startHighlightedIndex - newStart + startHighlightedIndex + (int) (maxSymbols * .9f))); } messageTrimmedToHighlight = charSequence; + messageTrimmedToHighlightCut = cut; } } } @@ -11031,11 +11104,21 @@ public float getProgress() { } private Boolean videoQualitiesCached; + public ArrayList videoQualities; public TLRPC.Document qualityToSave; + + public VideoPlayer.VideoUri highestQuality, thumbQuality; + public boolean hasVideoQualities() { if (videoQualitiesCached == null) { try { - videoQualitiesCached = messageOwner != null && VideoPlayer.hasQualities(currentAccount, messageOwner.media); + if (messageOwner == null || messageOwner.media == null || messageOwner.media.document == null || messageOwner.media.alt_documents.isEmpty()) { + return videoQualitiesCached = false; + } + videoQualities = VideoPlayer.getQualities(currentAccount, messageOwner != null ? messageOwner.media : null); + videoQualitiesCached = videoQualities != null && videoQualities.size() > 1; + highestQuality = VideoPlayer.getQualityForPlayer(videoQualities); + thumbQuality = VideoPlayer.getQualityForThumb(videoQualities); } catch (Exception e) { FileLog.e(e); videoQualitiesCached = false; @@ -11047,4 +11130,21 @@ public boolean hasVideoQualities() { public boolean isStarGiftAction() { return messageOwner != null && messageOwner.action instanceof TLRPC.TL_messageActionStarGift; } + + public boolean mediaExists() { + if (hasVideoQualities() && highestQuality != null) { + return highestQuality.isCached(); + } + return mediaExists; + } + + public void updateQualitiesCached(boolean useFileDatabaseQueue) { + if (videoQualities == null) return; + for (VideoPlayer.Quality q : videoQualities) { + for (VideoPlayer.VideoUri u : q.uris) { + u.updateCached(useFileDatabaseQueue); + } + } + } + } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java index 7054de54426..8921b854e8a 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java @@ -697,6 +697,7 @@ public boolean premiumPurchaseBlocked() { public int stargiftsMessageLengthMax; public int stargiftsConvertPeriodMax; public boolean videoIgnoreAltDocuments; + public boolean disableBotFullscreenBlur; public int checkResetLangpack; public boolean folderTags; @@ -965,7 +966,6 @@ public class SponsoredMessagesInfo { public Integer posts_between; public long loadTime; public boolean loading; - public boolean faked; } private class SendAsPeersInfo { @@ -1553,6 +1553,7 @@ public MessagesController(int num) { stargiftsMessageLengthMax = mainPreferences.getInt("stargiftsMessageLengthMax", 255); stargiftsConvertPeriodMax = mainPreferences.getInt("stargiftsConvertPeriodMax", isTest ? 300 : 90 * 86400); videoIgnoreAltDocuments = mainPreferences.getBoolean("videoIgnoreAltDocuments", false); + disableBotFullscreenBlur = mainPreferences.getBoolean("disableBotFullscreenBlur", false); storiesPosting = mainPreferences.getString("storiesPosting", "enabled"); storiesEntities = mainPreferences.getString("storiesEntities", "premium"); storiesExportNopublicLink = mainPreferences.getBoolean("storiesExportNopublicLink", false); @@ -2381,7 +2382,7 @@ public void removeFilter(DialogFilter filter) { private Runnable loadAppConfigRunnable = this::loadAppConfig; public void loadAppConfig() { - loadAppConfig(false); + loadAppConfig(true); } public void loadAppConfig(boolean force) { @@ -3738,6 +3739,17 @@ private void applyAppConfig(TLRPC.TL_jsonObject object) { } break; } + case "bot_fullscreen_blur_disable": { + if (value.value instanceof TLRPC.TL_jsonBool) { + TLRPC.TL_jsonBool bool = (TLRPC.TL_jsonBool) value.value; + if (bool.value != disableBotFullscreenBlur) { + disableBotFullscreenBlur = bool.value; + editor.putBoolean("disableBotFullscreenBlur", disableBotFullscreenBlur); + changed = true; + } + } + break; + } case "stories_posting": { if (value.value instanceof TLRPC.TL_jsonString) { TLRPC.TL_jsonString str = (TLRPC.TL_jsonString) value.value; @@ -4929,7 +4941,8 @@ private void resetAppConfig() { canEditFactcheck = false; starsLocked = true; factcheckLengthLimit = 1024; - mainPreferences.edit().remove("starsLocked").remove("getfileExperimentalParams").remove("smsjobsStickyNotificationEnabled").remove("channelRevenueWithdrawalEnabled").remove("showAnnualPerMonth").remove("canEditFactcheck").remove("factcheckLengthLimit").apply(); + videoIgnoreAltDocuments = false; + mainPreferences.edit().remove("starsLocked").remove("getfileExperimentalParams").remove("smsjobsStickyNotificationEnabled").remove("channelRevenueWithdrawalEnabled").remove("showAnnualPerMonth").remove("canEditFactcheck").remove("factcheckLengthLimit").remove("videoIgnoreAltDocuments").apply(); } private boolean savePremiumFeaturesPreviewOrder(String key, SparseIntArray array, SharedPreferences.Editor editor, ArrayList value) { @@ -8320,6 +8333,10 @@ public void deleteMessages(ArrayList messages, ArrayList randoms, } public void deleteMessages(ArrayList messages, ArrayList randoms, TLRPC.EncryptedChat encryptedChat, long dialogId, boolean forAll, int mode, boolean cacheOnly, long taskId, TLObject taskRequest, int topicId) { + deleteMessages(messages, randoms, encryptedChat, dialogId, forAll, mode, cacheOnly, taskId, taskRequest, topicId, false, 0); + } + + public void deleteMessages(ArrayList messages, ArrayList randoms, TLRPC.EncryptedChat encryptedChat, long dialogId, boolean forAll, int mode, boolean cacheOnly, long taskId, TLObject taskRequest, int topicId, boolean movedToScheduled, int movedToScheduledMessageId) { final boolean scheduled = mode == ChatActivity.MODE_SCHEDULED; final boolean quickReplies = mode == ChatActivity.MODE_QUICK_REPLIES; if ((messages == null || messages.isEmpty()) && taskId == 0) { @@ -8365,7 +8382,7 @@ public void deleteMessages(ArrayList messages, ArrayList randoms, getMessagesStorage().markMessagesAsDeleted(dialogId, messages, true, forAll, 0, topicId); getMessagesStorage().updateDialogsWithDeletedMessages(dialogId, channelId, messages, null, true); } - getNotificationCenter().postNotificationName(NotificationCenter.messagesDeleted, messages, channelId, scheduled); + getNotificationCenter().postNotificationName(NotificationCenter.messagesDeleted, messages, channelId, scheduled, false, movedToScheduled, movedToScheduledMessageId); } else { if (taskRequest instanceof TLRPC.TL_channels_deleteMessages) { channelId = ((TLRPC.TL_channels_deleteMessages) taskRequest).channel.channel_id; @@ -10816,6 +10833,14 @@ public void processLoadedMessages(TLRPC.messages_Messages messagesRes, int resCo }); } + public void forceNoReload(long dialogId, int mode) { + if (mode == ChatActivity.MODE_SCHEDULED) { + lastScheduledServerQueryTime.put(dialogId, SystemClock.elapsedRealtime()); + } else if (mode == ChatActivity.MODE_DEFAULT) { + lastServerQueryTime.put(dialogId, SystemClock.elapsedRealtime()); + } + } + public void loadHintDialogs() { if (!hintDialogs.isEmpty() || TextUtils.isEmpty(installReferer)) { return; @@ -16648,6 +16673,7 @@ public boolean processUpdateArray(ArrayList updates, ArrayList> deletedMessages = null; LongSparseArray> deletedQuickReplyMessages = null; LongSparseArray> scheduledDeletedMessages = null; + LongSparseArray> scheduledDeletedMessagesSent = null; LongSparseArray> groupSpeakingActions = null; LongSparseIntArray importingActions = null; LongSparseIntArray clearHistoryMessages = null; @@ -16994,12 +17020,24 @@ public boolean processUpdateArray(ArrayList updates, ArrayList(); } long id = MessageObject.getPeerId(update.peer); - ArrayList arrayList = scheduledDeletedMessages.get(MessageObject.getPeerId(update.peer)); + ArrayList arrayList = scheduledDeletedMessages.get(id); if (arrayList == null) { arrayList = new ArrayList<>(); scheduledDeletedMessages.put(id, arrayList); } arrayList.addAll(update.messages); + + if (!update.sent_messages.isEmpty()) { + if (scheduledDeletedMessagesSent == null) { + scheduledDeletedMessagesSent = new LongSparseArray<>(); + } + ArrayList arrayList2 = scheduledDeletedMessagesSent.get(id); + if (arrayList2 == null) { + arrayList2 = new ArrayList<>(); + scheduledDeletedMessagesSent.put(id, arrayList2); + } + arrayList2.addAll(update.sent_messages); + } } else if (baseUpdate instanceof TLRPC.TL_updateUserTyping || baseUpdate instanceof TLRPC.TL_updateChatUserTyping || baseUpdate instanceof TLRPC.TL_updateChannelUserTyping) { long userId; long chatId; @@ -17891,6 +17929,10 @@ public boolean processUpdateArray(ArrayList updates, ArrayList updates, ArrayList updates, ArrayList> deletedMessagesFinal = deletedMessages; LongSparseArray> deletedQuickRepliesMessagesFinal = deletedQuickReplyMessages; LongSparseArray> scheduledDeletedMessagesFinal = scheduledDeletedMessages; + LongSparseArray> scheduledDeletedMessagesSentFinal = scheduledDeletedMessagesSent; LongSparseIntArray clearHistoryMessagesFinal = clearHistoryMessages; getMessagesStorage().getStorageQueue().postRunnable(() -> AndroidUtilities.runOnUIThread(() -> { int updateMask = 0; @@ -19023,8 +19066,8 @@ public boolean processUpdateArray(ArrayList updates, ArrayList sentMessageIds = scheduledDeletedMessagesSentFinal != null ? scheduledDeletedMessagesSentFinal.get(key) : null; + getNotificationCenter().postNotificationName(NotificationCenter.messagesDeleted, arrayList, DialogObject.isChatDialog(key) && ChatObject.isChannel(getChat(-key)) ? -key : 0, true, false, false, 0, sentMessageIds); } } if (clearHistoryMessagesFinal != null) { @@ -19315,36 +19358,19 @@ public void markReactionsAsRead(long dialogId, long topicId) { } public SponsoredMessagesInfo getSponsoredMessages(long dialogId) { -// for (int i = 0; i < sponsoredMessages.size(); ++i) { -// if (sponsoredMessages.valueAt(i).messages != null && !sponsoredMessages.valueAt(i).messages.isEmpty()) { -// SponsoredMessagesInfo info = sponsoredMessages.valueAt(i); -// if (info.faked) { -// return info; -// } -// info.loading = true; -// info.faked = true; -// AndroidUtilities.runOnUIThread(() -> { -// info.loading = false; -// getNotificationCenter().postNotificationName(NotificationCenter.didLoadSponsoredMessages, dialogId, info.messages); -// AndroidUtilities.runOnUIThread(() -> { info.faked = false; }, 500); -// }, 1500); -// return null; -// } -// } SponsoredMessagesInfo info = sponsoredMessages.get(dialogId); if (info != null && (info.loading || Math.abs(SystemClock.elapsedRealtime() - info.loadTime) <= 5 * 60 * 1000)) { return info; } - TLRPC.Chat chat = getChat(-dialogId); - if (!ChatObject.isChannel(chat)) { + if (dialogId < 0 ? !ChatObject.isChannel(getChat(-dialogId)) : !UserObject.isBot(getUser(dialogId))) { return null; } info = new SponsoredMessagesInfo(); info.loading = true; sponsoredMessages.put(dialogId, info); SponsoredMessagesInfo infoFinal = info; - TLRPC.TL_channels_getSponsoredMessages req = new TLRPC.TL_channels_getSponsoredMessages(); - req.channel = getInputChannel(chat); + TLRPC.TL_messages_getSponsoredMessages req = new TLRPC.TL_messages_getSponsoredMessages(); + req.peer = getInputPeer(dialogId); getConnectionsManager().sendRequest(req, (response, error) -> { ArrayList result; Integer posts_between; @@ -21918,6 +21944,9 @@ public void openApp(TLRPC.User bot, int classGuid) { openApp(null, bot, null, classGuid, null); } public void openApp(BaseFragment _fragment, TLRPC.User bot, String param, int classGuid, Browser.Progress progress) { + openApp(_fragment, bot, param, classGuid, progress, false, false); + } + public void openApp(BaseFragment _fragment, TLRPC.User bot, String param, int classGuid, Browser.Progress progress, boolean botCompact, boolean botFullscreen) { if (bot == null) return; boolean[] cancelled = new boolean[] { false }; @@ -21939,50 +21968,50 @@ public void openApp(BaseFragment _fragment, TLRPC.User bot, String param, int cl fragment = ((ActionBarLayout) fragment.getParentLayout()).getSheetFragment(); } AndroidUtilities.hideKeyboard(fragment.getFragmentView()); - WebViewRequestProps props = WebViewRequestProps.of(currentAccount, bot.id, bot.id, null, null, BotWebViewAttachedSheet.TYPE_WEB_VIEW_BOT_MAIN, 0, false, null, false, param, bot, 0, false); + WebViewRequestProps props = WebViewRequestProps.of(currentAccount, bot.id, bot.id, null, null, BotWebViewAttachedSheet.TYPE_WEB_VIEW_BOT_MAIN, 0, false, null, false, param, bot, 0, botCompact, botFullscreen); if (LaunchActivity.instance != null && LaunchActivity.instance.getBottomSheetTabs() != null && LaunchActivity.instance.getBottomSheetTabs().tryReopenTab(props) != null) { return; } - if (AndroidUtilities.isTablet()) { +// if (AndroidUtilities.isTablet() || true) { BotWebViewSheet webViewSheet = new BotWebViewSheet(fragment.getContext(), fragment.getResourceProvider()); webViewSheet.setDefaultFullsize(true); - webViewSheet.setNeedsContext(true); + webViewSheet.setNeedsContext(false); webViewSheet.setParentActivity(fragment.getParentActivity()); webViewSheet.requestWebView(fragment, props); webViewSheet.show(); - } else { - BotWebViewAttachedSheet sheet = fragment.createBotViewer(); - sheet.setDefaultFullsize(true); - sheet.setNeedsContext(false); - sheet.setParentActivity(fragment.getParentActivity()); - sheet.requestWebView(fragment, props); - sheet.show(); - } +// } else { +// BotWebViewAttachedSheet sheet = fragment.createBotViewer(); +// sheet.setDefaultFullsize(true); +// sheet.setNeedsContext(false); +// sheet.setParentActivity(fragment.getParentActivity()); +// sheet.requestWebView(fragment, props); +// sheet.show(); +// } } else if (botInfo[0] != null && botInfo[0].menu_button instanceof TL_bots.TL_botMenuButton) { if (fragment.getParentLayout() instanceof ActionBarLayout) { fragment = ((ActionBarLayout) fragment.getParentLayout()).getSheetFragment(); } TL_bots.TL_botMenuButton btn = (TL_bots.TL_botMenuButton) botInfo[0].menu_button; AndroidUtilities.hideKeyboard(fragment.getFragmentView()); - WebViewRequestProps props = WebViewRequestProps.of(currentAccount, bot.id, bot.id, btn.text, btn.url, BotWebViewAttachedSheet.TYPE_BOT_MENU_BUTTON, 0, false, null, false, param, bot, 0, false); + WebViewRequestProps props = WebViewRequestProps.of(currentAccount, bot.id, bot.id, btn.text, btn.url, BotWebViewAttachedSheet.TYPE_BOT_MENU_BUTTON, 0, false, null, false, param, bot, 0, botCompact, botFullscreen); if (LaunchActivity.instance != null && LaunchActivity.instance.getBottomSheetTabs() != null && LaunchActivity.instance.getBottomSheetTabs().tryReopenTab(props) != null) { return; } - if (AndroidUtilities.isTablet()) { +// if (AndroidUtilities.isTablet() || true) { BotWebViewSheet webViewSheet = new BotWebViewSheet(fragment.getContext(), fragment.getResourceProvider()); webViewSheet.setDefaultFullsize(false); webViewSheet.setNeedsContext(true); webViewSheet.setParentActivity(fragment.getParentActivity()); webViewSheet.requestWebView(fragment, props); webViewSheet.show(); - } else { - BotWebViewAttachedSheet sheet = fragment.createBotViewer(); - sheet.setDefaultFullsize(false); - sheet.setNeedsContext(false); - sheet.setParentActivity(fragment.getParentActivity()); - sheet.requestWebView(fragment, props); - sheet.show(); - } +// } else { +// BotWebViewAttachedSheet sheet = fragment.createBotViewer(); +// sheet.setDefaultFullsize(false); +// sheet.setNeedsContext(false); +// sheet.setParentActivity(fragment.getParentActivity()); +// sheet.requestWebView(fragment, props); +// sheet.show(); +// } } else { fragment.presentFragment(ChatActivity.of(bot.id)); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java index bbba1e7e671..1a7842e56e5 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java @@ -44,6 +44,7 @@ import org.telegram.ui.Adapters.DialogsSearchAdapter; import org.telegram.ui.ChatActivity; import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; +import org.telegram.ui.Components.VideoPlayer; import org.telegram.ui.DialogsActivity; import org.telegram.ui.EditWidgetActivity; @@ -11927,12 +11928,20 @@ private void putMessagesInternal(ArrayList messages, boolean with } data.reuse(); - if (downloadMask != 0 && (message.peer_id.channel_id == 0 || message.post) && message.date >= getConnectionsManager().getCurrentTime() - 60 * 60 && getDownloadController().canDownloadMedia(message) == 1) { - if (message.media instanceof TLRPC.TL_messageMediaPhoto || message.media instanceof TLRPC.TL_messageMediaDocument || message.media instanceof TLRPC.TL_messageMediaWebPage) { + if (downloadMask != 0 && (message.peer_id.channel_id == 0 || message.post) && message.date >= getConnectionsManager().getCurrentTime() - 15 * 60 && getDownloadController().canDownloadMedia(message) == 1) { + final long dialogId = MessageObject.getDialogId(message); + if (getDialogFolderIdInternal(dialogId) != 1 && (message.media instanceof TLRPC.TL_messageMediaPhoto || message.media instanceof TLRPC.TL_messageMediaDocument || message.media instanceof TLRPC.TL_messageMediaWebPage)) { int type = 0; long id = 0; TLRPC.MessageMedia object = null; TLRPC.Document document = MessageObject.getDocument(message); + ArrayList qualities = VideoPlayer.getQualities(currentAccount, message.media); + if (qualities != null) { + VideoPlayer.VideoUri v = VideoPlayer.getQualityForThumb(qualities); + if (v != null) { + document = v.document; + } + } TLRPC.Photo photo = MessageObject.getPhoto(message); if (MessageObject.isVoiceMessage(message)) { id = document.id; @@ -16097,6 +16106,32 @@ private void putDialogsInternal(TLRPC.messages_Dialogs dialogs, int check) { } } + private int getDialogFolderIdInternal(long dialogId) { + SQLiteCursor cursor = null; + try { + int folderId; + if (unknownDialogsIds.get(dialogId) != null) { + folderId = -1; + } else { + cursor = database.queryFinalized("SELECT folder_id FROM dialogs WHERE did = ?", dialogId); + if (cursor.next()) { + folderId = cursor.intValue(0); + } else { + folderId = -1; + } + cursor.dispose(); + } + return folderId; + } catch (Exception e) { + checkSQLException(e); + } finally { + if (cursor != null) { + cursor.dispose(); + } + } + return 0; + } + public void getDialogFolderId(long dialogId, IntCallback callback) { storageQueue.postRunnable(() -> { SQLiteCursor cursor = null; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java index e514aa40d01..631ecb8a6aa 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java @@ -146,24 +146,18 @@ public class NotificationCenter { public static final int quickRepliesDeleted = totalEvents++; public static final int bookmarkAdded = totalEvents++; public static final int starReactionAnonymousUpdate = totalEvents++; - public static final int businessLinksUpdated = totalEvents++; public static final int businessLinkCreated = totalEvents++; public static final int needDeleteBusinessLink = totalEvents++; - public static final int messageTranslated = totalEvents++; public static final int messageTranslating = totalEvents++; public static final int dialogIsTranslatable = totalEvents++; public static final int dialogTranslate = totalEvents++; - public static final int didGenerateFingerprintKeyPair = totalEvents++; - public static final int walletPendingTransactionsChanged = totalEvents++; public static final int walletSyncProgressChanged = totalEvents++; - public static final int httpFileDidLoad = totalEvents++; public static final int httpFileDidFailedLoad = totalEvents++; - public static final int didUpdateConnectionState = totalEvents++; public static final int fileUploaded = totalEvents++; @@ -267,6 +261,8 @@ public class NotificationCenter { public static final int starGiftsLoaded = totalEvents++; public static final int starUserGiftsLoaded = totalEvents++; public static final int starGiftSoldOut = totalEvents++; + public static final int updateStories = totalEvents++; + public static final int botDownloadsUpdate = 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 f2ed65d2c4a..9b3ce3460b9 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java @@ -2179,6 +2179,8 @@ private String getShortStringForMessage(MessageObject messageObject, String[] us peername = peerchat == null ? "" : peerchat.title; } return LocaleController.formatPluralStringComma("BoostingReceivedStars", (int) action.stars, peername); + } else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionPaymentRefunded) { + return messageObject.messageText.toString(); } } else { if (messageObject.isMediaEmpty()) { @@ -4071,7 +4073,7 @@ private void showOrUpdateNotification(boolean notifyAboutLast) { return; } if (replace) { - if (chat != null) { + if (chat != null && allowSummary) { message = message.replace(" @ " + name, ""); } else { if (text[0]) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/PushListenerController.java b/TMessagesProj/src/main/java/org/telegram/messenger/PushListenerController.java index 952666bbca8..f7682a6bfc0 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/PushListenerController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/PushListenerController.java @@ -152,7 +152,6 @@ public static void processRemoteMessage(@PushType int pushType, String data, lon buffer.readBytes(strBytes, true); jsonString = new String(strBytes); JSONObject json = new JSONObject(jsonString); -// FileLog.d("FCM DATA: " + jsonString); if (ApplicationLoader.applicationLoaderInstance != null && ApplicationLoader.applicationLoaderInstance.consumePush(currentAccount, json)) { countDownLatch.countDown(); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java b/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java index 6e4fd910fa8..5d7a5595291 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java @@ -2321,7 +2321,8 @@ public int sendMessage(ArrayList messages, final long peer, boole TLRPC.Update update = updates.updates.get(a1); if (update instanceof TLRPC.TL_updateNewMessage || update instanceof TLRPC.TL_updateNewChannelMessage || update instanceof TLRPC.TL_updateNewScheduledMessage || update instanceof TLRPC.TL_updateQuickReplyMessage) { - boolean currentSchedule = scheduleDate != 0; + boolean currentSchedule = false; + boolean scheduled = scheduleDate != 0; updates.updates.remove(a1); a1--; @@ -2330,9 +2331,11 @@ public int sendMessage(ArrayList messages, final long peer, boole TLRPC.TL_updateNewMessage updateNewMessage = (TLRPC.TL_updateNewMessage) update; message = updateNewMessage.message; getMessagesController().processNewDifferenceParams(-1, updateNewMessage.pts, -1, updateNewMessage.pts_count); + currentSchedule = false; } else if (update instanceof TLRPC.TL_updateNewScheduledMessage) { TLRPC.TL_updateNewScheduledMessage updateNewMessage = (TLRPC.TL_updateNewScheduledMessage) update; message = updateNewMessage.message; + currentSchedule = true; } else if (update instanceof TLRPC.TL_updateQuickReplyMessage) { QuickRepliesController.getInstance(currentAccount).processUpdate(update, null, 0); TLRPC.TL_updateQuickReplyMessage updateQuickReplyMessage = (TLRPC.TL_updateQuickReplyMessage) update; @@ -2341,6 +2344,7 @@ public int sendMessage(ArrayList messages, final long peer, boole TLRPC.TL_updateNewChannelMessage updateNewChannelMessage = (TLRPC.TL_updateNewChannelMessage) update; message = updateNewChannelMessage.message; getMessagesController().processNewChannelDifferenceParams(updateNewChannelMessage.pts, updateNewChannelMessage.pts_count, message.peer_id.channel_id); + currentSchedule = false; } if (scheduledOnline && message.date != 0x7FFFFFFE) { currentSchedule = false; @@ -2380,17 +2384,19 @@ public int sendMessage(ArrayList messages, final long peer, boole newMsgObj1.id = message.id; sentCount++; - if (scheduleDate != 0 && !currentSchedule) { + if (scheduled != currentSchedule) { + final int fromMode = scheduled ? ChatActivity.MODE_SCHEDULED : 0; + final int toMode = currentSchedule ? ChatActivity.MODE_SCHEDULED : 0; AndroidUtilities.runOnUIThread(() -> { - ArrayList messageIds = new ArrayList<>(); - messageIds.add(oldId); - getMessagesController().deleteMessages(messageIds, null, null, newMsgObj1.dialog_id, newMsgObj1.quick_reply_shortcut_id, false, ChatActivity.MODE_SCHEDULED); getMessagesStorage().getStorageQueue().postRunnable(() -> { - getMessagesStorage().putMessages(sentMessages, true, false, false, 0, 0, 0); + getMessagesStorage().putMessages(sentMessages, true, false, false, 0, toMode, 0); AndroidUtilities.runOnUIThread(() -> { + ArrayList messageIds = new ArrayList<>(); + messageIds.add(oldId); + getMessagesController().deleteMessages(messageIds, null, null, newMsgObj1.dialog_id, false, fromMode, false, 0, null, 0, toMode == ChatActivity.MODE_SCHEDULED, message.id); ArrayList messageObjects = new ArrayList<>(); messageObjects.add(new MessageObject(msgObj.currentAccount, msgObj.messageOwner, true, true)); - getMessagesController().updateInterfaceWithMessages(newMsgObj1.dialog_id, messageObjects, 0); + getMessagesController().updateInterfaceWithMessages(newMsgObj1.dialog_id, messageObjects, toMode); getMediaDataController().increasePeerRaiting(newMsgObj1.dialog_id); processSentMessage(oldId); removeFromSendingMessages(oldId, scheduleDate != 0); @@ -2851,6 +2857,9 @@ public void editMessage(MessageObject messageObject, TLRPC.TL_photo photo, Video delayedMessage.performMediaUpload = performMediaUpload; } } + if (inputMedia instanceof TLRPC.TL_inputMediaEmpty && (messageObject.type == MessageObject.TYPE_TEXT || messageObject.type == MessageObject.TYPE_EMOJIS)) { + inputMedia = null; + } TLObject reqSend; @@ -2861,6 +2870,8 @@ public void editMessage(MessageObject messageObject, TLRPC.TL_photo photo, Video if (inputMedia != null) { request.flags |= 16384; request.media = inputMedia; + } else if (!messageObject.editingMessageSearchWebPage) { + request.no_webpage = true; } if (messageObject.scheduled) { request.schedule_date = messageObject.messageOwner.date; @@ -6094,6 +6105,7 @@ protected void performSendMessageRequestMulti(final TLObject request, final Arra final TLRPC.Updates updates = (TLRPC.Updates) response; ArrayList updatesArr = ((TLRPC.Updates) response).updates; LongSparseArray> channelReplies = null; + boolean currentSchedule = scheduled; for (int a = 0; a < updatesArr.size(); a++) { TLRPC.Update update = updatesArr.get(a); if (update instanceof TLRPC.TL_updateMessageID) { @@ -6102,6 +6114,7 @@ protected void performSendMessageRequestMulti(final TLObject request, final Arra updatesArr.remove(a); a--; } else if (update instanceof TLRPC.TL_updateNewMessage) { + currentSchedule = false; final TLRPC.TL_updateNewMessage newMessage = (TLRPC.TL_updateNewMessage) update; newMessages.put(newMessage.message.id, newMessage.message); Utilities.stageQueue.postRunnable(() -> getMessagesController().processNewDifferenceParams(-1, newMessage.pts, -1, newMessage.pts_count)); @@ -6145,11 +6158,13 @@ protected void performSendMessageRequestMulti(final TLObject request, final Arra }); } } else if (update instanceof TLRPC.TL_updateNewScheduledMessage) { + currentSchedule = true; final TLRPC.TL_updateNewScheduledMessage newMessage = (TLRPC.TL_updateNewScheduledMessage) update; newMessages.put(newMessage.message.id, newMessage.message); updatesArr.remove(a); a--; } else if (update instanceof TLRPC.TL_updateQuickReplyMessage) { + currentSchedule = false; QuickRepliesController.getInstance(currentAccount).processUpdate(update, msgObjs.isEmpty() ? null : msgObjs.get(0).getQuickReplyName(), msgObjs.isEmpty() ? null : msgObjs.get(0).getQuickReplyId()); final TLRPC.TL_updateQuickReplyMessage newMessage = (TLRPC.TL_updateQuickReplyMessage) update; newMessages.put(newMessage.message.id, newMessage.message); @@ -6162,6 +6177,11 @@ protected void performSendMessageRequestMulti(final TLObject request, final Arra getNotificationCenter().postNotificationName(NotificationCenter.didUpdateMessagesViews, null, null, channelReplies, true); } + final int[] totalSent = new int[1]; + final int[] done = new int[1]; + totalSent[0] = 0; + done[0] = 0; + final ArrayList oldIds = new ArrayList<>(); for (int i = 0; i < msgObjs.size(); i++) { final MessageObject msgObj = msgObjs.get(i); final String originalPath = originalPaths.get(i); @@ -6212,22 +6232,31 @@ protected void performSendMessageRequestMulti(final TLObject request, final Arra break; } + final boolean finalCurrentSchedule = currentSchedule; if (!isSentError) { + totalSent[0]++; + oldIds.add(oldId); getStatsController().incrementSentItemsCount(ApplicationLoader.getCurrentNetworkType(), StatsController.TYPE_MESSAGES, 1); newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SENT; - getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer, oldId, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, grouped_id, existFlags, scheduled); - getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer2, oldId, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, grouped_id, existFlags, scheduled); + getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer, oldId, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, grouped_id, existFlags, currentSchedule); + getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer2, oldId, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, grouped_id, existFlags, currentSchedule); getMessagesStorage().getStorageQueue().postRunnable(() -> { - int mode = scheduled ? ChatActivity.MODE_SCHEDULED : 0; + int mode = finalCurrentSchedule ? ChatActivity.MODE_SCHEDULED : 0; if (newMsgObj.quick_reply_shortcut_id != 0 || newMsgObj.quick_reply_shortcut != null) { mode = ChatActivity.MODE_QUICK_REPLIES; } - getMessagesStorage().updateMessageStateAndId(newMsgObj.random_id, MessageObject.getPeerId(newMsgObj.peer_id), oldId, newMsgObj.id, 0, false, scheduled ? 1 : 0, newMsgObj.quick_reply_shortcut_id); + getMessagesStorage().updateMessageStateAndId(newMsgObj.random_id, MessageObject.getPeerId(newMsgObj.peer_id), oldId, newMsgObj.id, 0, false, mode, newMsgObj.quick_reply_shortcut_id); getMessagesStorage().putMessages(sentMessages, true, false, false, 0, mode, newMsgObj.quick_reply_shortcut_id); AndroidUtilities.runOnUIThread(() -> { + done[0]++; + if (done[0] == totalSent[0] && scheduled != finalCurrentSchedule) { + long dialogId = msgObj.getDialogId(); + final int scheduledMessageId = finalCurrentSchedule && newMessages.size() > 1 ? newMessages.keyAt(0) : 0; + getMessagesController().deleteMessages(oldIds, null, null, dialogId, false, scheduled ? ChatActivity.MODE_SCHEDULED : 0, false, 0, null, 0, finalCurrentSchedule && !scheduled, scheduledMessageId); + } getMediaDataController().increasePeerRaiting(newMsgObj.dialog_id); - getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer, oldId, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, grouped_id, existFlags, scheduled); - getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer2, oldId, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, grouped_id, existFlags, scheduled); + getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer, oldId, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, grouped_id, existFlags, finalCurrentSchedule); + getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer2, oldId, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, grouped_id, existFlags, finalCurrentSchedule); processSentMessage(oldId); removeFromSendingMessages(oldId, scheduled); }); @@ -6432,7 +6461,7 @@ protected void performSendMessageRequest(final TLObject req, final MessageObject sentMessages.add(message = newMessage.message); Utilities.stageQueue.postRunnable(() -> getMessagesController().processNewDifferenceParams(-1, newMessage.pts, -1, newMessage.pts_count)); updatesArr.remove(a); - break; + a--; } else if (update instanceof TLRPC.TL_updateNewChannelMessage) { final TLRPC.TL_updateNewChannelMessage newMessage = (TLRPC.TL_updateNewChannelMessage) update; long channelId = MessagesController.getUpdateChannelId(newMessage); @@ -6462,6 +6491,7 @@ protected void performSendMessageRequest(final TLObject req, final MessageObject sentMessages.add(message = newMessage.message); Utilities.stageQueue.postRunnable(() -> getMessagesController().processNewChannelDifferenceParams(newMessage.pts, newMessage.pts_count, newMessage.message.peer_id.channel_id)); updatesArr.remove(a); + currentSchedule = false; a--; if (newMessage.message.pinned) { Utilities.stageQueue.postRunnable(() -> { @@ -6470,18 +6500,39 @@ protected void performSendMessageRequest(final TLObject req, final MessageObject getMessagesStorage().updatePinnedMessages(-channelId, mids, true, -1, 0, false, null); }); } - break; } else if (update instanceof TLRPC.TL_updateNewScheduledMessage) { final TLRPC.TL_updateNewScheduledMessage newMessage = (TLRPC.TL_updateNewScheduledMessage) update; + for (int i = 0; i < sentMessages.size(); ++i) { + if (sentMessages.get(i).id == newMessage.message.id) { + sentMessages.remove(i); + break; + } + } sentMessages.add(message = newMessage.message); updatesArr.remove(a); - break; + a--; + currentSchedule = true; } else if (update instanceof TLRPC.TL_updateQuickReplyMessage) { QuickRepliesController.getInstance(currentAccount).processUpdate(update, msgObj.getQuickReplyName(), msgObj.getQuickReplyId()); final TLRPC.TL_updateQuickReplyMessage newMessage = (TLRPC.TL_updateQuickReplyMessage) update; sentMessages.add(message = newMessage.message); updatesArr.remove(a); - break; + a--; + } else if (update instanceof TLRPC.TL_updateDeleteScheduledMessages) { + final TLRPC.TL_updateDeleteScheduledMessages upd = (TLRPC.TL_updateDeleteScheduledMessages) update; + if (msgObj.getDialogId() == DialogObject.getPeerDialogId(upd.peer)) { + for (int msg_id : upd.messages) { + for (int i = 0; i < sentMessages.size(); ++i) { + if (sentMessages.get(i).id == msg_id) { + sentMessages.remove(i); + break; + } + } + } + currentSchedule = false; + updatesArr.remove(a); + a--; + } } } if (channelReplies != null) { @@ -6538,16 +6589,18 @@ protected void performSendMessageRequest(final TLObject req, final MessageObject if (!isSentError) { getStatsController().incrementSentItemsCount(ApplicationLoader.getCurrentNetworkType(), StatsController.TYPE_MESSAGES, 1); newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SENT; - if (scheduled && !currentSchedule) { + if (scheduled != currentSchedule) { + final boolean finalCurrentSchedule = currentSchedule; ArrayList messageIds = new ArrayList<>(); messageIds.add(oldId); - getMessagesController().deleteMessages(messageIds, null, null, newMsgObj.dialog_id, 0, false, ChatActivity.MODE_SCHEDULED); + ArrayList messageObjects = new ArrayList<>(); + messageObjects.add(new MessageObject(msgObj.currentAccount, msgObj.messageOwner, true, true)); getMessagesStorage().getStorageQueue().postRunnable(() -> { - getMessagesStorage().putMessages(sentMessages, true, false, false, 0, false, 0, 0); + getMessagesStorage().putMessages(sentMessages, true, false, false, 0, false, !scheduled ? ChatActivity.MODE_SCHEDULED : ChatActivity.MODE_DEFAULT, 0); AndroidUtilities.runOnUIThread(() -> { - ArrayList messageObjects = new ArrayList<>(); - messageObjects.add(new MessageObject(msgObj.currentAccount, msgObj.messageOwner, true, true)); - getMessagesController().updateInterfaceWithMessages(newMsgObj.dialog_id, messageObjects, 0); + final int scheduledMessageId = finalCurrentSchedule && newMsgObj != null ? newMsgObj.id : 0; + getMessagesController().deleteMessages(messageIds, null, null, newMsgObj.dialog_id, false, scheduled ? ChatActivity.MODE_SCHEDULED : ChatActivity.MODE_DEFAULT, false, 0, null, 0, !scheduled && finalCurrentSchedule, scheduledMessageId); + getMessagesController().updateInterfaceWithMessages(newMsgObj.dialog_id, messageObjects, finalCurrentSchedule ? ChatActivity.MODE_SCHEDULED : ChatActivity.MODE_DEFAULT); getMediaDataController().increasePeerRaiting(newMsgObj.dialog_id); processSentMessage(oldId); removeFromSendingMessages(oldId, scheduled); @@ -8775,6 +8828,29 @@ public static void prepareSendingMedia(AccountInstance accountInstance, ArrayLis } fillVideoAttribute(info.path, attributeVideo, null); } + } else if (!document.thumbs.isEmpty()) { + if (info.thumbPath != null) { + thumb = BitmapFactory.decodeFile(info.thumbPath); + } + if (thumb == null) { + thumb = createVideoThumbnailAtTime(info.path, startTime); + if (thumb == null) { + thumb = createVideoThumbnail(info.path, MediaStore.Video.Thumbnails.MINI_KIND); + } + } + + TLRPC.PhotoSize size = null; + if (thumb != null) { + int side = isEncrypted || info.ttl != 0 ? 90 : Math.max(thumb.getWidth(), thumb.getHeight()); + size = ImageLoader.scaleAndSaveImage(null, thumb, videoEditedInfo != null && videoEditedInfo.isSticker ? Bitmap.CompressFormat.WEBP : Bitmap.CompressFormat.JPEG, false, side, side, side > 90 ? 80 : 55, isEncrypted, 0, 0, false); + if (size != null && size.location != null) { + thumbKey = getKeyForPhotoSize(accountInstance, size, null, true, false); + } + } + if (size != null) { + document.thumbs.add(size); + document.flags |= 1; + } } if (videoEditedInfo != null && videoEditedInfo.muted) { boolean found = false; @@ -9475,6 +9551,38 @@ public static void prepareSendingVideo(AccountInstance accountInstance, String v } fillVideoAttribute(videoPath, attributeVideo, null); } + } else if (document.thumbs.isEmpty()) { + if (videoEditedInfo != null && videoEditedInfo.notReadyYet) { + thumb = videoEditedInfo.thumb; + } + if (thumb == null) { + thumb = createVideoThumbnailAtTime(videoPath, startTime); + } + if (thumb == null) { + thumb = createVideoThumbnail(videoPath, MediaStore.Video.Thumbnails.MINI_KIND); + } + int side = isEncrypted || ttl != 0 ? 90 : 320; + TLRPC.PhotoSize size = ImageLoader.scaleAndSaveImage(thumb, side, side, side > 90 ? 80 : 55, isEncrypted); + if (thumb != null && size != null) { + if (isRound) { + if (isEncrypted) { + thumb = Bitmap.createScaledBitmap(thumb, 90, 90, true); + Utilities.blurBitmap(thumb, 7, Build.VERSION.SDK_INT < 21 ? 0 : 1, thumb.getWidth(), thumb.getHeight(), thumb.getRowBytes()); + Utilities.blurBitmap(thumb, 7, Build.VERSION.SDK_INT < 21 ? 0 : 1, thumb.getWidth(), thumb.getHeight(), thumb.getRowBytes()); + Utilities.blurBitmap(thumb, 7, Build.VERSION.SDK_INT < 21 ? 0 : 1, thumb.getWidth(), thumb.getHeight(), thumb.getRowBytes()); + thumbKey = String.format(size.location.volume_id + "_" + size.location.local_id + "@%d_%d_b2", (int) (AndroidUtilities.roundMessageSize / AndroidUtilities.density), (int) (AndroidUtilities.roundMessageSize / AndroidUtilities.density)); + } else { + Utilities.blurBitmap(thumb, 3, Build.VERSION.SDK_INT < 21 ? 0 : 1, thumb.getWidth(), thumb.getHeight(), thumb.getRowBytes()); + thumbKey = String.format(size.location.volume_id + "_" + size.location.local_id + "@%d_%d_b", (int) (AndroidUtilities.roundMessageSize / AndroidUtilities.density), (int) (AndroidUtilities.roundMessageSize / AndroidUtilities.density)); + } + } else { + thumb = null; + } + } + if (size != null) { + document.thumbs.add(size); + document.flags |= 1; + } } if (videoEditedInfo != null && videoEditedInfo.needConvert()) { String fileName = Integer.MIN_VALUE + "_" + SharedConfig.getLastLocalId() + ".mp4"; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ShortcutResultReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/ShortcutResultReceiver.java new file mode 100644 index 00000000000..a1265a569cb --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ShortcutResultReceiver.java @@ -0,0 +1,22 @@ +package org.telegram.messenger; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public class ShortcutResultReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + final int currentAccount = intent.getIntExtra("account", UserConfig.selectedAccount); + final String req_id = intent.getStringExtra("req_id"); + + Utilities.Callback callback = MediaDataController.getInstance(currentAccount).shortcutCallbacks.remove(req_id); + if (callback != null) { + AndroidUtilities.runOnUIThread(() -> { + callback.run(true); + }); + } + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/UserNameResolver.java b/TMessagesProj/src/main/java/org/telegram/messenger/UserNameResolver.java index ce0a8f6bfb1..f1c9744eec3 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/UserNameResolver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/UserNameResolver.java @@ -27,13 +27,13 @@ public class UserNameResolver { LruCache resolvedCache = new LruCache<>(100); HashMap>> resolvingConsumers = new HashMap<>(); - public void resolve(String username, Consumer resolveConsumer) { + public int resolve(String username, Consumer resolveConsumer) { CachedPeer cachedPeer = resolvedCache.get(username); if (cachedPeer != null) { if (System.currentTimeMillis() - cachedPeer.time < CACHE_TIME) { resolveConsumer.accept(cachedPeer.peerId); FileLog.d("resolve username from cache " + username + " " + cachedPeer.peerId); - return; + return -1; } else { resolvedCache.remove(username); } @@ -42,7 +42,7 @@ public void resolve(String username, Consumer resolveConsumer) { ArrayList> consumers = resolvingConsumers.get(username); if (consumers != null) { consumers.add(resolveConsumer); - return; + return -1; } consumers = new ArrayList<>(); consumers.add(resolveConsumer); @@ -59,7 +59,7 @@ public void resolve(String username, Consumer resolveConsumer) { resolveUsername.username = username; req = resolveUsername; } - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + return ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { ArrayList> finalConsumers = resolvingConsumers.remove(username); if (finalConsumers == null) { return; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/UserObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/UserObject.java index 65b32f7ae4d..33fe7f1cd0a 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/UserObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/UserObject.java @@ -41,6 +41,10 @@ public static boolean isAnonymous(TLRPC.User user) { return user != null && user.id == ANONYMOUS; } + public static boolean isBot(TLRPC.User user) { + return user != null && user.bot; + } + public static boolean isReplyUser(long did) { return did == 708513 || did == REPLY_BOT; } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/audioinfo/AudioInfo.java b/TMessagesProj/src/main/java/org/telegram/messenger/audioinfo/AudioInfo.java index a0bcd5fe6e5..9f241ed1d5c 100755 --- a/TMessagesProj/src/main/java/org/telegram/messenger/audioinfo/AudioInfo.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/audioinfo/AudioInfo.java @@ -149,7 +149,10 @@ public static AudioInfo getAudioInfo(File file) { OtherAudioInfo info = new OtherAudioInfo(file); if (info.failed) return null; return info; - } else if (file.getAbsolutePath().endsWith("mp3")) { + } else if (file.getAbsolutePath().endsWith("mp3") || ( + (header[0] == 'I' && header[1] == 'D' && header[2] == '3') || + (header[0] == 'T' && header[1] == 'A' && header[2] == 'G') + )) { return new MP3Info(input, file.length()); } else { OtherAudioInfo info = new OtherAudioInfo(file); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/browser/Browser.java b/TMessagesProj/src/main/java/org/telegram/messenger/browser/Browser.java index e5d08155d74..55e5516c2f8 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/browser/Browser.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/browser/Browser.java @@ -255,14 +255,14 @@ public void onEnd(Runnable onEndListener) { } public static void openUrl(final Context context, Uri uri, final boolean allowCustom, boolean tryTelegraph) { - openUrl(context, uri, allowCustom, tryTelegraph, false, null, null, false, true); + openUrl(context, uri, allowCustom, tryTelegraph, false, null, null, false, true, false); } public static void openUrl(final Context context, Uri uri, final boolean allowCustom, boolean tryTelegraph, Progress inCaseLoading) { - openUrl(context, uri, allowCustom, tryTelegraph, false, inCaseLoading, null, false, true); + openUrl(context, uri, allowCustom, tryTelegraph, false, inCaseLoading, null, false, true, false); } - public static void openUrl(final Context context, Uri uri, boolean _allowCustom, boolean tryTelegraph, boolean forceNotInternalForApps, Progress inCaseLoading, String browser, boolean allowIntent, boolean allowInAppBrowser) { + public static void openUrl(final Context context, Uri uri, boolean _allowCustom, boolean tryTelegraph, boolean forceNotInternalForApps, Progress inCaseLoading, String browser, boolean allowIntent, boolean allowInAppBrowser, boolean forceRequest) { if (context == null || uri == null) { return; } @@ -397,7 +397,7 @@ public static void openUrl(final Context context, Uri uri, boolean _allowCustom, ); final boolean isIntentScheme = uri.getScheme() != null && uri.getScheme().equalsIgnoreCase("intent"); if (internalUri && LaunchActivity.instance != null) { - openAsInternalIntent(LaunchActivity.instance, uri.toString(), forceNotInternalForApps, inCaseLoading); + openAsInternalIntent(LaunchActivity.instance, uri.toString(), forceNotInternalForApps, forceRequest, inCaseLoading); } else { if (inappBrowser) { if (!openInExternalApp(context, uri.toString(), allowIntent)) { @@ -420,15 +420,15 @@ public static void openUrl(final Context context, Uri uri, boolean _allowCustom, } public static boolean openAsInternalIntent(Context context, String url) { - return openAsInternalIntent(context, url, false, null); + return openAsInternalIntent(context, url, false, false, null); } public static boolean openAsInternalIntent(Context context, String url, Browser.Progress progress) { - return openAsInternalIntent(context, url, false, progress); + return openAsInternalIntent(context, url, false, false, progress); } public static boolean openAsInternalIntent(Context context, String url, boolean forceNotInternalForApps) { - return openAsInternalIntent(context, url, forceNotInternalForApps, null); + return openAsInternalIntent(context, url, forceNotInternalForApps, false, null); } - public static boolean openAsInternalIntent(Context context, String url, boolean forceNotInternalForApps, Browser.Progress progress) { + public static boolean openAsInternalIntent(Context context, String url, boolean forceNotInternalForApps, boolean forceRequest, Progress progress) { if (url == null) return false; LaunchActivity activity = null; if (AndroidUtilities.findActivity(context) instanceof LaunchActivity) { @@ -445,6 +445,7 @@ public static boolean openAsInternalIntent(Context context, String url, boolean intent.putExtra(android.provider.Browser.EXTRA_CREATE_NEW_TAB, true); intent.putExtra(android.provider.Browser.EXTRA_APPLICATION_ID, context.getPackageName()); intent.putExtra(LaunchActivity.EXTRA_FORCE_NOT_INTERNAL_APPS, forceNotInternalForApps); + intent.putExtra(LaunchActivity.EXTRA_FORCE_REQUEST, forceRequest); activity.onNewIntent(intent, progress); return true; } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/secretmedia/ExtendedDefaultDataSource.java b/TMessagesProj/src/main/java/org/telegram/messenger/secretmedia/ExtendedDefaultDataSource.java index 37e9eac469d..c42a6219702 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/secretmedia/ExtendedDefaultDataSource.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/secretmedia/ExtendedDefaultDataSource.java @@ -23,17 +23,24 @@ import com.google.android.exoplayer2.upstream.FileDataSource; import com.google.android.exoplayer2.upstream.RawResourceDataSource; import com.google.android.exoplayer2.upstream.TransferListener; +import com.google.android.exoplayer2.upstream.cache.Cache; +import com.google.android.exoplayer2.upstream.cache.CacheSpan; +import com.google.android.exoplayer2.upstream.cache.ContentMetadata; +import com.google.android.exoplayer2.upstream.cache.ContentMetadataMutations; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Util; import org.telegram.messenger.FileStreamLoadOperation; +import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.NavigableSet; +import java.util.Set; public final class ExtendedDefaultDataSource implements DataSource { @@ -314,4 +321,103 @@ private void maybeAddListenerToDataSource( dataSource.addTransferListener(listener); } } + + + private final Cache cache = new Cache() { + @Override + public long getUid() { + return 0; + } + + @Override + public void release() { + + } + + @Override + public NavigableSet addListener(String key, Listener listener) { + return null; + } + + @Override + public void removeListener(String key, Listener listener) { + + } + + @Override + public NavigableSet getCachedSpans(String key) { + return null; + } + + @Override + public Set getKeys() { + return null; + } + + @Override + public long getCacheSpace() { + return 0; + } + + @Override + public CacheSpan startReadWrite(String key, long position, long length) throws InterruptedException, CacheException { + return null; + } + + @Nullable + @Override + public CacheSpan startReadWriteNonBlocking(String key, long position, long length) throws CacheException { + return null; + } + + @Override + public File startFile(String key, long position, long length) throws CacheException { + return null; + } + + @Override + public void commitFile(File file, long length) throws CacheException { + + } + + @Override + public void releaseHoleSpan(CacheSpan holeSpan) { + + } + + @Override + public void removeResource(String key) { + + } + + @Override + public void removeSpan(CacheSpan span) { + + } + + @Override + public boolean isCached(String key, long position, long length) { + return false; + } + + @Override + public long getCachedLength(String key, long position, long length) { + return 0; + } + + @Override + public long getCachedBytes(String key, long position, long length) { + return 0; + } + + @Override + public void applyContentMetadataMutations(String key, ContentMetadataMutations mutations) throws CacheException { + + } + + @Override + public ContentMetadata getContentMetadata(String key) { + return null; + } + }; } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/OldVideoPlayerRewinder.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/OldVideoPlayerRewinder.java new file mode 100644 index 00000000000..c7eed728e58 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/OldVideoPlayerRewinder.java @@ -0,0 +1,242 @@ +package org.telegram.messenger.video; + +import com.google.android.exoplayer2.C; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.ui.Components.PhotoViewerWebView; +import org.telegram.ui.Components.VideoForwardDrawable; +import org.telegram.ui.Components.VideoPlayer; + +public class OldVideoPlayerRewinder { + + public int rewindCount; + private boolean rewindForward; + public boolean rewindByBackSeek; + private long startRewindFrom; + private Runnable updateRewindRunnable; + private long rewindLastTime; + private long rewindLastUpdatePlayerTime; + private long rewindBackSeekPlayerPosition; + private float playSpeed = 1f; + + private VideoPlayer videoPlayer; + private PhotoViewerWebView webView; + + private final Runnable backSeek = new Runnable() { + @Override + public void run() { + if (videoPlayer == null && webView == null) { + return; + } + long duration = getDuration(); + if (duration == 0 || duration == C.TIME_UNSET) { + rewindLastTime = System.currentTimeMillis(); + return; + } + + long t = System.currentTimeMillis(); + long dt = t - rewindLastTime; + rewindLastTime = t; + if (rewindCount == 1) { + dt *= 3; + } else if (rewindCount == 2) { + dt *= 6; + } else { + dt *= 12; + } + if (rewindForward) { + rewindBackSeekPlayerPosition += dt; + } else { + rewindBackSeekPlayerPosition -= dt; + } + if (rewindBackSeekPlayerPosition < 0) { + rewindBackSeekPlayerPosition = 0; + } else if (rewindBackSeekPlayerPosition > duration) { + rewindBackSeekPlayerPosition = duration; + } + if (rewindByBackSeek && rewindLastTime - rewindLastUpdatePlayerTime > 350) { + rewindLastUpdatePlayerTime = rewindLastTime; + seekTo(rewindBackSeekPlayerPosition); + } + + long timeDiff = rewindBackSeekPlayerPosition - startRewindFrom; + float progress = rewindBackSeekPlayerPosition / (float) getDuration(); + updateRewindProgressUi(timeDiff, progress, rewindByBackSeek); + + if (rewindBackSeekPlayerPosition == 0 || rewindBackSeekPlayerPosition >= duration) { + if (rewindByBackSeek) { + rewindLastUpdatePlayerTime = rewindLastTime; + seekTo(rewindBackSeekPlayerPosition); + } + cancelRewind(); + } + if (rewindCount > 0) { + AndroidUtilities.runOnUIThread(backSeek, 16); + } + } + }; + + public void startRewind(PhotoViewerWebView webView, boolean forward, float playbackSpeed) { + this.webView = webView; + this.playSpeed = playbackSpeed; + rewindForward = forward; + cancelRewind(); + incrementRewindCount(); + } + + public void startRewind(VideoPlayer videoPlayer, boolean forward, float playbackSpeed) { + this.videoPlayer = videoPlayer; + this.playSpeed = playbackSpeed; + rewindForward = forward; + cancelRewind(); + incrementRewindCount(); + } + + public void cancelRewind() { + if (rewindCount != 0) { + rewindCount = 0; + + if (videoPlayer != null || webView != null) { + if (rewindByBackSeek) { + seekTo(rewindBackSeekPlayerPosition); + } else { + long current = getCurrentPosition(); + seekTo(current); + } + setPlaybackSpeed(playSpeed); + } + } + AndroidUtilities.cancelRunOnUIThread(backSeek); + + if (updateRewindRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(updateRewindRunnable); + updateRewindRunnable = null; + } + + onRewindCanceled(); + } + + private void incrementRewindCount() { + if (videoPlayer == null && webView == null) { + return; + } + rewindCount++; + boolean needUpdate = false; + if (rewindCount == 1) { + if (rewindForward && isPlaying()) { + rewindByBackSeek = false; + } else { + rewindByBackSeek = true; + } + } + if (rewindForward && !rewindByBackSeek) { + if (rewindCount == 1) { + setPlaybackSpeed(4); + needUpdate = true; + } else if (rewindCount == 2) { + setPlaybackSpeed(7); + needUpdate = true; + } else { + setPlaybackSpeed(13); + } + } else { + if (rewindCount == 1 || rewindCount == 2) { + needUpdate = true; + } + } + + + if (rewindCount == 1) { + rewindBackSeekPlayerPosition = getCurrentPosition(); + rewindLastTime = System.currentTimeMillis(); + rewindLastUpdatePlayerTime = rewindLastTime; + startRewindFrom = getCurrentPosition(); + onRewindStart(rewindForward); + } + + AndroidUtilities.cancelRunOnUIThread(backSeek); + AndroidUtilities.runOnUIThread(backSeek); + + if (needUpdate) { + if (updateRewindRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(updateRewindRunnable); + } + AndroidUtilities.runOnUIThread(updateRewindRunnable = () -> { + updateRewindRunnable = null; + incrementRewindCount(); + }, 2000); + } + } + + + protected void updateRewindProgressUi(long timeDiff, float progress, boolean rewindByBackSeek) { + + } + + protected void onRewindStart(boolean rewindForward) { + + } + + protected void onRewindCanceled() { + + } + + private void seekTo(long position) { + if (webView != null) { + webView.seekTo(position); + } else { + if (videoPlayer == null) { + return; + } + videoPlayer.seekTo(position); + } + } + + private void setPlaybackSpeed(float speed) { + if (webView != null) { + webView.setPlaybackSpeed(speed); + } else { + if (videoPlayer == null) { + return; + } + videoPlayer.setPlaybackSpeed(speed); + } + } + + private long getCurrentPosition() { + if (webView != null) { + return webView.getCurrentPosition(); + } else { + if (videoPlayer == null) { + return 0; + } + return videoPlayer.getCurrentPosition(); + } + } + + private long getDuration() { + if (webView != null) { + return webView.getVideoDuration(); + } else { + if (videoPlayer == null) { + return 0; + } + return videoPlayer.getDuration(); + } + } + + private boolean isPlaying() { + if (webView != null) { + return webView.isPlaying(); + } else { + if (videoPlayer == null) { + return false; + } + return videoPlayer.isPlaying(); + } + } + + public float getVideoProgress() { + return rewindBackSeekPlayerPosition / (float) getDuration(); + } +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/VideoFramesRewinder.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/VideoFramesRewinder.java new file mode 100644 index 00000000000..a7ed226b0e5 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/VideoFramesRewinder.java @@ -0,0 +1,265 @@ +package org.telegram.messenger.video; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.util.Log; +import android.view.View; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.Utilities; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedFileDrawable; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.TreeSet; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; + +public class VideoFramesRewinder { + + private int maxFramesCount; + private int maxFrameSide; + + private final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); + private View parentView; + int w, h; + + public VideoFramesRewinder() { + switch (SharedConfig.getDevicePerformanceClass()) { + case SharedConfig.PERFORMANCE_CLASS_HIGH: + maxFramesCount = 400; + maxFrameSide = 720; + break; + case SharedConfig.PERFORMANCE_CLASS_AVERAGE: + maxFramesCount = 200; + maxFrameSide = 580; + break; + default: + maxFramesCount = 100; + maxFrameSide = 480; + break; + } + } + + public void draw(Canvas canvas, int w, int h) { + this.w = w; + this.h = h; + if (ptr != 0 && currentFrame != null) { + canvas.save(); + canvas.scale(w / (float) currentFrame.bitmap.getWidth(), h / (float) currentFrame.bitmap.getHeight()); + canvas.drawBitmap(currentFrame.bitmap, 0, 0, paint); + canvas.restore(); + } + } + + private long ptr; + private final int[] meta = new int[6]; + + public boolean isReady() { + return ptr != 0; + } + + public void setup(File file) { + if (file == null) { + release(); + return; + } + stop.set(false); + ptr = AnimatedFileDrawable.createDecoder(file.getAbsolutePath(), meta, UserConfig.selectedAccount, 0, null, true); + } + + private final ArrayList freeFrames = new ArrayList<>(); + private final TreeSet frames = new TreeSet((a, b) -> { + return (int) (a.position - b.position); + }); + private Frame currentFrame; + + private class Frame { + long position; + Bitmap bitmap; + } + + private AtomicBoolean stop = new AtomicBoolean(false); + private AtomicLong until = new AtomicLong(0); + private boolean isPreparing; + private long lastSeek; + private float lastSpeed = 1.0f; + private long prepareToMs; + private float prepareWithSpeed; + private boolean destroyAfterPrepare; + private Runnable prepareRunnable = () -> { + final ArrayList newFrames = new ArrayList<>(); + + final long start = System.currentTimeMillis(); + + final int fps = meta[4]; + int w = Math.min(this.w / 4, meta[0]), h = Math.min(this.h / 4, meta[1]); + if (w > maxFrameSide || h > maxFrameSide) { + final float scale = (float) maxFrameSide / Math.max(w, h); + w = (int) (w * scale); + h = (int) (h * scale); + } + final long toMs = prepareToMs; + AnimatedFileDrawable.seekToMs(ptr, toMs - (long) (350 * prepareWithSpeed), meta, false); + long ms = meta[3]; + int triesCount = 0; + for (int i = 0; meta[3] <= until.get() && i < maxFramesCount && !stop.get(); ++i) { + long nextms = (long) (ms + (1000.0f / fps) * prepareWithSpeed); + Frame frame; + if (!freeFrames.isEmpty()) { + frame = freeFrames.remove(0); + } else { + frame = new Frame(); + } + if (frame.bitmap == null || frame.bitmap.getWidth() != w || frame.bitmap.getHeight() != h) { + AndroidUtilities.recycleBitmap(frame.bitmap); + try { + frame.bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); + } catch (OutOfMemoryError e) { + FileLog.d("[VideoFramesRewinder] failed to create bitmap: out of memory"); + break; + } + } + while (meta[3] + (long) Math.ceil(1000.0f / fps) < nextms) { + AnimatedFileDrawable.getVideoFrame(ptr, null, meta, 0, true, 0, meta[4], false); + } + if (0 == AnimatedFileDrawable.getVideoFrame(ptr, frame.bitmap, meta, frame.bitmap.getRowBytes(), true, 0, meta[4], false)) { + triesCount++; + if (triesCount > 6) break; + continue; + } + ms = frame.position = meta[3]; + newFrames.add(frame); + } + + AndroidUtilities.runOnUIThread(() -> { + FileLog.d("[VideoFramesRewinder] total prepare of " + newFrames.size() + " took " + (System.currentTimeMillis() - start) + "ms"); + if (!newFrames.isEmpty()) { + FileLog.d("[VideoFramesRewinder] prepared from " + newFrames.get(0).position + "ms to " + newFrames.get(newFrames.size() - 1).position + "ms (requested up to "+prepareToMs+"ms)"); + } + isPreparing = false; + final Iterator i = frames.iterator(); + while (i.hasNext()) { + final Frame f = i.next(); + if (currentFrame != f && f.position > lastSeek) { + if (freeFrames.size() > 20) { + AndroidUtilities.recycleBitmap(f.bitmap); + } else { + freeFrames.add(f); + } + i.remove(); + } + } + while (!newFrames.isEmpty() && frames.size() < maxFramesCount) { + frames.add(newFrames.remove(newFrames.size() - 1)); + } + if (newFrames.size() > 0) { + FileLog.d("[VideoFramesRewinder] prepared "+newFrames.size()+" more frames than I could fit :("); + } + + if (destroyAfterPrepare) { + release(); + stop.set(false); + } + }); + }; + private void prepare(long toMs) { + if (isPreparing) { + return; + } + FileLog.d("[VideoFramesRewinder] starting preparing " + toMs + "ms"); + isPreparing = true; + prepareToMs = toMs; + prepareWithSpeed = lastSpeed; + Utilities.themeQueue.postRunnable(prepareRunnable); + } + + public void seek(long position, float currentSpeed) { + if (ptr == 0) return; + + lastSeek = position; + lastSpeed = currentSpeed; + until.set(position); + + final Iterator i = frames.iterator(); + final ArrayList pastPositions = new ArrayList<>(); + while (i.hasNext()) { + final Frame f = i.next(); + pastPositions.add(f.position); + if (Math.abs(f.position - position) < 25 * currentSpeed) { + if (currentFrame != f) { + FileLog.d("[VideoFramesRewinder] found a frame " + f.position + "ms to fit to "+position+"ms from " + frames.size() + " frames"); + currentFrame = f; + invalidate(); + + int deleted = 0; + while (i.hasNext()) { + i.next(); + i.remove(); + deleted++; + } + if (deleted > 0) { + FileLog.d("[VideoFramesRewinder] also deleted " + deleted + " frames after this frame"); + } + } + for (int j = pastPositions.size() - 2; j >= 0; --j) { + final long next = pastPositions.get(j + 1); + final long pos = pastPositions.get(j); + if (Math.abs(next - pos) > 25 * currentSpeed) { + prepare(pos); + return; + } + } + prepare(Math.max(0, frames.first().position - 20)); + return; + } + } + FileLog.d("[VideoFramesRewinder] didn't find a frame, wanting to prepare " + position + "ms"); + prepare(Math.max(0, position)); + } + + public void clearCurrent() { + if (currentFrame != null) { + currentFrame = null; + invalidate(); + } + } + + public void release() { + if (isPreparing) { + stop.set(true); + destroyAfterPrepare = true; + return; + } + AnimatedFileDrawable.destroyDecoder(ptr); + ptr = 0; + destroyAfterPrepare = false; + clearCurrent(); + until.set(0); + + for (Frame f : frames) { + AndroidUtilities.recycleBitmap(f.bitmap); + } + frames.clear(); + for (Frame f : freeFrames) { + AndroidUtilities.recycleBitmap(f.bitmap); + } + freeFrames.clear(); + } + + public void setParentView(View view) { + parentView = view; + } + + private void invalidate() { + if (parentView != null) { + parentView.invalidate(); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/VideoPlayerRewinder.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/VideoPlayerRewinder.java index b11c69b10e2..a66294414d9 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/video/VideoPlayerRewinder.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/VideoPlayerRewinder.java @@ -1,9 +1,15 @@ package org.telegram.messenger.video; +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.util.Log; + import com.google.android.exoplayer2.C; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.Utilities; import org.telegram.ui.Components.PhotoViewerWebView; +import org.telegram.ui.Components.SeekSpeedDrawable; import org.telegram.ui.Components.VideoForwardDrawable; import org.telegram.ui.Components.VideoPlayer; @@ -11,16 +17,27 @@ public class VideoPlayerRewinder { public int rewindCount; private boolean rewindForward; + private boolean fastSeeking; public boolean rewindByBackSeek; private long startRewindFrom; private Runnable updateRewindRunnable; private long rewindLastTime; private long rewindLastUpdatePlayerTime; - private long rewindBackSeekPlayerPosition; + private long rewindBackSeekLastPlayerPosition; + private long rewindBackSeekPlayerPosition = -1; private float playSpeed = 1f; + private boolean wasMuted; + private boolean wasPaused; + + private float value; private VideoPlayer videoPlayer; private PhotoViewerWebView webView; + private VideoFramesRewinder framesRewinder; + + public VideoPlayerRewinder(VideoFramesRewinder framesRewinder) { + this.framesRewinder = framesRewinder; + } private final Runnable backSeek = new Runnable() { @Override @@ -34,29 +51,20 @@ public void run() { return; } - long t = System.currentTimeMillis(); - long dt = t - rewindLastTime; - rewindLastTime = t; - if (rewindCount == 1) { - dt *= 3; - } else if (rewindCount == 2) { - dt *= 6; - } else { - dt *= 12; - } - if (rewindForward) { - rewindBackSeekPlayerPosition += dt; - } else { - rewindBackSeekPlayerPosition -= dt; - } - if (rewindBackSeekPlayerPosition < 0) { - rewindBackSeekPlayerPosition = 0; - } else if (rewindBackSeekPlayerPosition > duration) { - rewindBackSeekPlayerPosition = duration; - } - if (rewindByBackSeek && rewindLastTime - rewindLastUpdatePlayerTime > 350) { + final long now = System.currentTimeMillis(); + long dt = now - rewindLastTime; + rewindLastTime = now; + final float speed = Math.max(0, -getRewindSpeed() * playSpeed); + dt *= speed; + rewindBackSeekPlayerPosition -= dt; + rewindBackSeekPlayerPosition = Utilities.clamp(rewindBackSeekPlayerPosition, duration, 0); + if (rewindByBackSeek && getCurrentPosition() > rewindBackSeekPlayerPosition && rewindLastTime - rewindLastUpdatePlayerTime > 10) { rewindLastUpdatePlayerTime = rewindLastTime; - seekTo(rewindBackSeekPlayerPosition); + if (framesRewinder != null) { + framesRewinder.seek(rewindBackSeekPlayerPosition, Math.abs(speed)); + } else { + seekTo(rewindBackSeekPlayerPosition, false); + } } long timeDiff = rewindBackSeekPlayerPosition - startRewindFrom; @@ -66,109 +74,178 @@ public void run() { if (rewindBackSeekPlayerPosition == 0 || rewindBackSeekPlayerPosition >= duration) { if (rewindByBackSeek) { rewindLastUpdatePlayerTime = rewindLastTime; - seekTo(rewindBackSeekPlayerPosition); + seekTo(rewindBackSeekPlayerPosition, false); } cancelRewind(); } - if (rewindCount > 0) { + if (rewinding && getRewindSpeed() < 0) { AndroidUtilities.runOnUIThread(backSeek, 16); } } }; - public void startRewind(PhotoViewerWebView webView, boolean forward, float playbackSpeed) { + public boolean rewinding; + private float x; + private SeekSpeedDrawable seekSpeedDrawable; + + public void startRewind(PhotoViewerWebView webView, boolean forward, float initialX, float playbackSpeed, SeekSpeedDrawable seekSpeedDrawable) { + cancelRewind(); + this.videoPlayer = null; + this.webView = null; + if (framesRewinder != null) { + framesRewinder.release(); + } + rewindByBackSeek = forward; + rewinding = true; + rewindBackSeekPlayerPosition = -1; this.webView = webView; + this.seekSpeedDrawable = seekSpeedDrawable; this.playSpeed = playbackSpeed; - rewindForward = forward; - cancelRewind(); - incrementRewindCount(); + this.wasMuted = false; + this.wasPaused = webView != null && !webView.isPlaying(); + fastSeeking = false; + rewindLastUpdatePlayerTime = 0; + x = initialX; + value = forward ? getValueBySpeed(2.0f) : getValueBySpeed(-2.0f); + rewindBackSeekLastPlayerPosition = -100; + if (seekSpeedDrawable != null) { + seekSpeedDrawable.setSpeed(getRewindSpeed(), false); + seekSpeedDrawable.setShown(true, true); + } } - public void startRewind(VideoPlayer videoPlayer, boolean forward, float playbackSpeed) { + public void startRewind(VideoPlayer videoPlayer, boolean forward, float initialX, float playbackSpeed, SeekSpeedDrawable seekSpeedDrawable) { + cancelRewind(); + this.videoPlayer = null; + this.webView = null; + if (framesRewinder != null) { + framesRewinder.release(); + } + rewindByBackSeek = forward; + rewinding = true; + rewindBackSeekPlayerPosition = -1; this.videoPlayer = videoPlayer; + this.seekSpeedDrawable = seekSpeedDrawable; this.playSpeed = playbackSpeed; - rewindForward = forward; - cancelRewind(); - incrementRewindCount(); + this.wasMuted = videoPlayer != null && videoPlayer.isMuted(); + this.wasPaused = videoPlayer != null && !videoPlayer.isPlaying(); + fastSeeking = false; + rewindLastUpdatePlayerTime = 0; + x = initialX; + value = forward ? getValueBySpeed(2.0f) : getValueBySpeed(-2.0f); + rewindBackSeekLastPlayerPosition = -100; + if (seekSpeedDrawable != null) { + seekSpeedDrawable.setSpeed(getRewindSpeed(), false); + seekSpeedDrawable.setShown(true, true); + } + updateRewindSpeed(); } - public void cancelRewind() { - if (rewindCount != 0) { - rewindCount = 0; + public float getRewindSpeed() { + float v = value; + v = v < 0.4f ? v - 1.9f : v; +// v /= 2.0f; +// v = v * v * v; + return Utilities.clamp(v, +10.0f, -6.0f); + } - if (videoPlayer != null || webView != null) { - if (rewindByBackSeek) { - seekTo(rewindBackSeekPlayerPosition); - } else { - long current = getCurrentPosition(); - seekTo(current); - } + public float getValueBySpeed(float speed) { + float value = speed; +// value = (float) Math.cbrt(value); +// value *= 2.0f; + if (value < -1.5f) { + value += 1.9f; + } + return value; + } + + public void updateRewindSpeed() { + final float rewindSpeed = getRewindSpeed(); + if (rewindSpeed < 0) { + if (!rewindByBackSeek) { + rewindByBackSeek = true; + rewindBackSeekPlayerPosition = getCurrentPosition(); + rewindLastTime = System.currentTimeMillis(); + AndroidUtilities.runOnUIThread(backSeek); + setMuted(true); + setPaused(true); setPlaybackSpeed(playSpeed); + if (framesRewinder != null && !framesRewinder.isReady() && videoPlayer != null) { + framesRewinder.setup(videoPlayer.getLowestFile()); + } } + } else { + if (rewindByBackSeek) { + rewindByBackSeek = false; + AndroidUtilities.cancelRunOnUIThread(backSeek); + setMuted(wasMuted || wasPaused); + setPaused(false); + if (videoPlayer != null && framesRewinder != null && rewindBackSeekPlayerPosition >= 0) { + videoPlayer.seekTo(rewindBackSeekPlayerPosition, false, () -> { + if (framesRewinder != null) { + framesRewinder.clearCurrent(); + } + }); + } + } + setPlaybackSpeed(playSpeed * rewindSpeed); } - AndroidUtilities.cancelRunOnUIThread(backSeek); + } - if (updateRewindRunnable != null) { - AndroidUtilities.cancelRunOnUIThread(updateRewindRunnable); - updateRewindRunnable = null; + public void setX(float x) { + float diff = this.x - x; + value -= diff / dp(40); + this.x = x; + + if (seekSpeedDrawable != null) { + seekSpeedDrawable.setSpeed(getRewindSpeed(), true); } - onRewindCanceled(); + updateRewindSpeed(); } - private void incrementRewindCount() { - if (videoPlayer == null && webView == null) { - return; - } - rewindCount++; - boolean needUpdate = false; - if (rewindCount == 1) { - if (rewindForward && isPlaying()) { - rewindByBackSeek = false; + public void cancelRewind() { + if (!rewinding) return; + + rewinding = false; + fastSeeking = false; + boolean awaitSeek = false; + if (videoPlayer != null || webView != null) { + if (rewindByBackSeek) { + if (videoPlayer != null && framesRewinder != null) { + awaitSeek = true; + videoPlayer.seekTo(rewindBackSeekPlayerPosition, false, () -> { + if (framesRewinder != null) { + framesRewinder.release(); + } + }); + } else { + seekTo(rewindBackSeekPlayerPosition, false); + } } else { - rewindByBackSeek = true; + seekTo(getCurrentPosition(), false); } + setPlaybackSpeed(playSpeed); } - if (rewindForward && !rewindByBackSeek) { - if (rewindCount == 1) { - setPlaybackSpeed(4); - needUpdate = true; - } else if (rewindCount == 2) { - setPlaybackSpeed(7); - needUpdate = true; - } else { - setPlaybackSpeed(13); - } - } else { - if (rewindCount == 1 || rewindCount == 2) { - needUpdate = true; - } + setMuted(wasMuted); + setPaused(wasPaused); + AndroidUtilities.cancelRunOnUIThread(backSeek); + if (framesRewinder != null && !awaitSeek) { + framesRewinder.release(); } - - if (rewindCount == 1) { - rewindBackSeekPlayerPosition = getCurrentPosition(); - rewindLastTime = System.currentTimeMillis(); - rewindLastUpdatePlayerTime = rewindLastTime; - startRewindFrom = getCurrentPosition(); - onRewindStart(rewindForward); + if (updateRewindRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(updateRewindRunnable); + updateRewindRunnable = null; } - AndroidUtilities.cancelRunOnUIThread(backSeek); - AndroidUtilities.runOnUIThread(backSeek); + onRewindCanceled(); - if (needUpdate) { - if (updateRewindRunnable != null) { - AndroidUtilities.cancelRunOnUIThread(updateRewindRunnable); - } - AndroidUtilities.runOnUIThread(updateRewindRunnable = () -> { - updateRewindRunnable = null; - incrementRewindCount(); - }, 2000); + if (seekSpeedDrawable != null) { + seekSpeedDrawable.setShown(false, true); } } - protected void updateRewindProgressUi(long timeDiff, float progress, boolean rewindByBackSeek) { } @@ -181,14 +258,34 @@ protected void onRewindCanceled() { } - private void seekTo(long position) { + private void seekTo(long position, boolean fast) { if (webView != null) { webView.seekTo(position); - } else { - if (videoPlayer == null) { - return; + } else if (videoPlayer != null) { + videoPlayer.seekTo(position, fast); + } + rewindBackSeekLastPlayerPosition = position; + } + + private void setMuted(boolean muted) { + if (videoPlayer != null) { + videoPlayer.setMute(muted); + } + } + + private void setPaused(boolean paused) { + if (webView != null) { + if (paused) { + webView.pauseVideo(); + } else { + webView.playVideo(); + } + } else if (videoPlayer != null) { + if (paused) { + videoPlayer.pause(); + } else { + videoPlayer.play(); } - videoPlayer.seekTo(position); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPActionsReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPActionsReceiver.java index 8e4044600d8..abe0a2ff619 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPActionsReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPActionsReceiver.java @@ -22,7 +22,7 @@ public void onReceive(Context context, Intent intent) { } else if ((packageName + ".ANSWER_CALL").equals(intent.getAction())) { VoIPPreNotificationService.answer(context); } else if ((packageName + ".HIDE_CALL").equals(intent.getAction())) { - VoIPPreNotificationService.dismiss(context); + VoIPPreNotificationService.dismiss(context, false); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPPreNotificationService.java b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPPreNotificationService.java index 5b9548ed10d..e92e5af4acb 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPPreNotificationService.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPPreNotificationService.java @@ -7,12 +7,9 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Person; -import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.content.pm.ServiceInfo; import android.graphics.Bitmap; import android.graphics.drawable.Icon; import android.media.AudioAttributes; @@ -21,15 +18,12 @@ import android.media.RingtoneManager; import android.net.Uri; import android.os.Build; -import android.os.IBinder; import android.os.Vibrator; import android.provider.Settings; import android.text.SpannableString; import android.text.TextUtils; import android.text.style.ForegroundColorSpan; -import androidx.annotation.Nullable; - import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.BuildVars; @@ -400,7 +394,7 @@ public static void show(Context context, Intent intent, TLRPC.PhoneCall call) { FileLog.d("VoIPPreNotification.show()"); if (call == null || intent == null) { - dismiss(context); + dismiss(context, false); FileLog.d("VoIPPreNotification.show(): call or intent is null"); return; } @@ -409,7 +403,7 @@ public static void show(Context context, Intent intent, TLRPC.PhoneCall call) { return; } - dismiss(context); + dismiss(context, false); pendingVoIP = intent; pendingCall = call; @@ -472,7 +466,7 @@ private static void acknowledge(Context context, int currentAccount, TLRPC.Phone if (currentState != null) { currentState.destroy(); } - dismiss(context); + dismiss(context, false); } else if (whenAcknowledged != null) { whenAcknowledged.run(); } @@ -493,7 +487,7 @@ public static boolean open(Context context) { context.startService(pendingVoIP); } pendingVoIP = null; - dismiss(context); + dismiss(context, true); return true; } @@ -533,7 +527,7 @@ public static void answer(Context context) { } pendingVoIP = null; } - dismiss(context); + dismiss(context, true); } public static void decline(Context context, int reason) { @@ -580,10 +574,10 @@ public static void decline(Context context, int reason) { } } }, ConnectionsManager.RequestFlagFailOnServerErrors); - dismiss(context); + dismiss(context, false); } - public static void dismiss(Context context) { + public static void dismiss(Context context, boolean answered) { FileLog.d("VoIPPreNotification.dismiss()"); pendingVoIP = null; pendingCall = null; @@ -593,6 +587,11 @@ public static void dismiss(Context context) { final NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); nm.cancel(VoIPService.ID_INCOMING_CALL_PRENOTIFICATION); stopRinging(); + if (!answered) { + for (int i = 0; i < UserConfig.MAX_ACCOUNT_COUNT; ++i) { + MessagesController.getInstance(i).ignoreSetOnline = false; + } + } // if (pendingNotificationService != null) { // context.stopService(pendingNotificationService); // } diff --git a/TMessagesProj/src/main/java/org/telegram/tgnet/ConnectionsManager.java b/TMessagesProj/src/main/java/org/telegram/tgnet/ConnectionsManager.java index 729639de4c1..b3eaabd805f 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/ConnectionsManager.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/ConnectionsManager.java @@ -2,7 +2,9 @@ import android.annotation.SuppressLint; import android.app.Activity; +import android.content.Context; import android.content.SharedPreferences; +import android.content.pm.InstallSourceInfo; import android.content.pm.PackageInfo; import android.os.AsyncTask; import android.os.Build; @@ -10,6 +12,7 @@ import android.text.TextUtils; import android.util.Base64; +import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.gms.tasks.Task; import com.google.android.play.core.integrity.IntegrityManager; import com.google.android.play.core.integrity.IntegrityManagerFactory; @@ -38,6 +41,7 @@ import org.telegram.messenger.StatsController; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; +import org.telegram.ui.Components.VideoPlayer; import org.telegram.ui.LoginActivity; import java.io.ByteArrayOutputStream; @@ -346,7 +350,7 @@ private void sendRequestInternal(TLObject object, RequestDelegate onComplete, Re object.freeResources(); long startRequestTime = 0; - if (BuildVars.DEBUG_PRIVATE_VERSION && BuildVars.LOGS_ENABLED) { + if (BuildVars.DEBUG_PRIVATE_VERSION && BuildVars.LOGS_ENABLED || (connectionType & ConnectionTypeDownload) != 0) { startRequestTime = System.currentTimeMillis(); } long finalStartRequestTime = startRequestTime; @@ -377,6 +381,10 @@ private void sendRequestInternal(TLObject object, RequestDelegate onComplete, Re FileLog.e(object + " got error " + error.code + " " + error.text); } } + if ((connectionType & ConnectionTypeDownload) != 0 && VideoPlayer.activePlayers.isEmpty()) { + long ping_time = native_getCurrentPingTime(currentAccount); + DefaultBandwidthMeter.getSingletonInstance(ApplicationLoader.applicationContext).onTransfer(responseSize, Math.max(0, (System.currentTimeMillis() - finalStartRequestTime) - ping_time)); + } if (BuildVars.DEBUG_PRIVATE_VERSION && !getUserConfig().isClientActivated() && error != null && error.code == 400 && Objects.equals(error.text, "CONNECTION_NOT_INITED")) { if (BuildVars.LOGS_ENABLED) { FileLog.d("Cleanup keys for " + currentAccount + " because of CONNECTION_NOT_INITED"); @@ -578,7 +586,18 @@ public void init(int version, int layer, int apiId, String deviceModel, String s } String installer = ""; try { - installer = ApplicationLoader.applicationContext.getPackageManager().getInstallerPackageName(ApplicationLoader.applicationContext.getPackageName()); + Context context = ApplicationLoader.applicationContext; + if (Build.VERSION.SDK_INT >= 30) { + InstallSourceInfo installSourceInfo = context.getPackageManager().getInstallSourceInfo(context.getPackageName()); + if (installSourceInfo != null) { + installer = installSourceInfo.getInitiatingPackageName(); + if (installer == null) { + installer = installSourceInfo.getInstallingPackageName(); + } + } + } else { + installer = context.getPackageManager().getInstallerPackageName(context.getPackageName()); + } } catch (Throwable ignore) { } @@ -906,6 +925,7 @@ public static void setProxySettings(boolean enabled, String address, int port, S public static native void native_resumeNetwork(int currentAccount, boolean partial); public static native long native_getCurrentTimeMillis(int currentAccount); public static native int native_getCurrentTime(int currentAccount); + public static native int native_getCurrentPingTime(int currentAccount); public static native int native_getCurrentDatacenterId(int currentAccount); public static native int native_getTimeDifference(int currentAccount); public static native void native_sendRequest(int currentAccount, long object, int flags, int datacenterId, int connectionType, boolean immediate, int requestToken); diff --git a/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java b/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java index 31d62588bc9..6385f9ec6e8 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java @@ -81,8 +81,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 = 191; - + public static final int LAYER = 193; public static abstract class EmailVerifyPurpose extends TLObject { @@ -5835,6 +5834,9 @@ public static PrivacyKey TLdeserialize(AbstractSerializedData stream, int constr case TL_privacyKeyBirthday.constructor: result = new TL_privacyKeyBirthday(); break; + case TL_privacyKeyStarGiftsAutoSave.constructor: + result = new TL_privacyKeyStarGiftsAutoSave(); + break; } if (result == null && exception) { throw new RuntimeException(String.format("can't parse magic %x in PrivacyKey", constructor)); @@ -5945,6 +5947,15 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_privacyKeyStarGiftsAutoSave extends PrivacyKey { + public static final int constructor = 0x2ca4fdf8; + + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static abstract class GeoPoint extends TLObject { public int flags; public double _long; @@ -8238,6 +8249,12 @@ public static PrivacyRule TLdeserialize(AbstractSerializedData stream, int const case 0xece9814b: result = new TL_privacyValueAllowPremium(); break; + case TL_privacyValueAllowBots.constructor: + result = new TL_privacyValueAllowBots(); + break; + case TL_privacyValueDisallowBots.constructor: + result = new TL_privacyValueDisallowBots(); + break; } if (result == null && exception) { throw new RuntimeException(String.format("can't parse magic %x in PrivacyRule", constructor)); @@ -8423,6 +8440,24 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_privacyValueAllowBots extends PrivacyRule { + public static final int constructor = 0x21461b5d; + + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_privacyValueDisallowBots extends PrivacyRule { + public static final int constructor = 0xf6a5f82f; + + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static class TL_pageTableCell extends TLObject { public static final int constructor = 0x34566b6a; @@ -12459,6 +12494,9 @@ public static InputPrivacyKey TLdeserialize(AbstractSerializedData stream, int c case TL_inputPrivacyKeyBirthday.constructor: result = new TL_inputPrivacyKeyBirthday(); break; + case TL_inputPrivacyKeyStarGiftsAutoSave.constructor: + result = new TL_inputPrivacyKeyStarGiftsAutoSave(); + break; } if (result == null && exception) { throw new RuntimeException(String.format("can't parse magic %x in InputPrivacyKey", constructor)); @@ -12551,6 +12589,15 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_inputPrivacyKeyStarGiftsAutoSave extends InputPrivacyKey { + public static final int constructor = 0xe1732341; + + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static class TL_inputPrivacyKeyProfilePhoto extends InputPrivacyKey { public static final int constructor = 0x5719bacc; @@ -28263,6 +28310,9 @@ public static MessageAction TLdeserialize(AbstractSerializedData stream, int con case TL_messageActionStarGift.constructor: result = new TL_messageActionStarGift(); break; + case TL_messageActionStarGift_layer192.constructor: + result = new TL_messageActionStarGift_layer192(); + break; } if (result == null && exception) { throw new RuntimeException(String.format("can't parse magic %x in MessageAction", constructor)); @@ -29989,7 +30039,7 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_invoice extends TLObject { - public static final int constructor = 0x5db95a15; + public static final int constructor = 0x49ee584; public int flags; public boolean test; @@ -30006,13 +30056,17 @@ public static class TL_invoice extends TLObject { public long max_tip_amount; public ArrayList suggested_tip_amounts = new ArrayList<>(); public String terms_url; + public int subscription_period; public static TL_invoice TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { TL_invoice result = null; switch (constructor) { - case 0x5db95a15: + case 0x49ee584: result = new TL_invoice(); break; + case 0x5db95a15: + result = new TL_invoice_layer193(); + break; case 0x3e85a91b: result = new TL_invoice_layer163(); break; @@ -30026,6 +30080,99 @@ public static TL_invoice TLdeserialize(AbstractSerializedData stream, int constr return result; } + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + test = (flags & 1) != 0; + name_requested = (flags & 2) != 0; + phone_requested = (flags & 4) != 0; + email_requested = (flags & 8) != 0; + shipping_address_requested = (flags & 16) != 0; + flexible = (flags & 32) != 0; + phone_to_provider = (flags & 64) != 0; + email_to_provider = (flags & 128) != 0; + recurring = (flags & 512) != 0; + currency = stream.readString(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_labeledPrice object = TL_labeledPrice.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + prices.add(object); + } + if ((flags & 256) != 0) { + max_tip_amount = stream.readInt64(exception); + } + if ((flags & 256) != 0) { + 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++) { + suggested_tip_amounts.add(stream.readInt64(exception)); + } + } + if ((flags & 1024) != 0) { + terms_url = stream.readString(exception); + } + if ((flags & 2048) != 0) { + subscription_period = stream.readInt32(exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = test ? (flags | 1) : (flags &~ 1); + flags = name_requested ? (flags | 2) : (flags &~ 2); + flags = phone_requested ? (flags | 4) : (flags &~ 4); + flags = email_requested ? (flags | 8) : (flags &~ 8); + flags = shipping_address_requested ? (flags | 16) : (flags &~ 16); + flags = flexible ? (flags | 32) : (flags &~ 32); + flags = phone_to_provider ? (flags | 64) : (flags &~ 64); + flags = email_to_provider ? (flags | 128) : (flags &~ 128); + flags = recurring ? (flags | 512) : (flags &~ 512); + stream.writeInt32(flags); + stream.writeString(currency); + stream.writeInt32(0x1cb5c415); + int count = prices.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + prices.get(a).serializeToStream(stream); + } + if ((flags & 256) != 0) { + stream.writeInt64(max_tip_amount); + } + if ((flags & 256) != 0) { + stream.writeInt32(0x1cb5c415); + count = suggested_tip_amounts.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt64(suggested_tip_amounts.get(a)); + } + } + if ((flags & 1024) != 0) { + stream.writeString(terms_url); + } + if ((flags & 2048) != 0) { + stream.writeInt32(subscription_period); + } + } + } + + public static class TL_invoice_layer193 extends TL_invoice { + public static final int constructor = 0x5db95a15; + public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); test = (flags & 1) != 0; @@ -33930,36 +34077,42 @@ public static abstract class InputPrivacyRule extends TLObject { public static InputPrivacyRule TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { InputPrivacyRule result = null; switch (constructor) { - case 0xd66b66c9: + case TL_inputPrivacyValueDisallowAll.constructor: result = new TL_inputPrivacyValueDisallowAll(); break; - case 0x90110467: + case TL_inputPrivacyValueDisallowUsers.constructor: result = new TL_inputPrivacyValueDisallowUsers(); break; - case 0xd09e07b: + case TL_inputPrivacyValueAllowContacts.constructor: result = new TL_inputPrivacyValueAllowContacts(); break; - case 0x840649cf: + case TL_inputPrivacyValueAllowChatParticipants.constructor: result = new TL_inputPrivacyValueAllowChatParticipants(); break; - case 0xba52007: + case TL_inputPrivacyValueDisallowContacts.constructor: result = new TL_inputPrivacyValueDisallowContacts(); break; - case 0x184b35ce: + case TL_inputPrivacyValueAllowAll.constructor: result = new TL_inputPrivacyValueAllowAll(); break; - case 0x131cc67f: + case TL_inputPrivacyValueAllowUsers.constructor: result = new TL_inputPrivacyValueAllowUsers(); break; - case 0xe94f0f86: + case TL_inputPrivacyValueDisallowChatParticipants.constructor: result = new TL_inputPrivacyValueDisallowChatParticipants(); break; - case 0x2f453e49: + case TL_inputPrivacyValueAllowCloseFriends.constructor: result = new TL_inputPrivacyValueAllowCloseFriends(); break; - case 0x77cdc9f1: + case TL_inputPrivacyValueAllowPremium.constructor: result = new TL_inputPrivacyValueAllowPremium(); break; + case TL_inputPrivacyValueAllowBots.constructor: + result = new TL_inputPrivacyValueAllowBots(); + break; + case TL_inputPrivacyValueDisallowBots.constructor: + result = new TL_inputPrivacyValueDisallowBots(); + break; } if (result == null && exception) { throw new RuntimeException(String.format("can't parse magic %x in InputPrivacyRule", constructor)); @@ -34151,6 +34304,22 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_inputPrivacyValueAllowBots extends InputPrivacyRule { + public static final int constructor = 0x5a4fcce5; + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_inputPrivacyValueDisallowBots extends InputPrivacyRule { + public static final int constructor = 0xc4e57915; + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static class TL_maskCoords extends TLObject { public static final int constructor = 0xaed6dbb2; @@ -36515,6 +36684,9 @@ public static Update TLdeserialize(AbstractSerializedData stream, int constructo case TL_updatePaidReactionPrivacy.constructor: result = new TL_updatePaidReactionPrivacy(); break; + case TL_updateBotSubscriptionExpire.constructor: + result = new TL_updateBotSubscriptionExpire(); + break; } if (result == null && ApplicationLoader.applicationLoaderInstance != null) { result = ApplicationLoader.applicationLoaderInstance.parseTLUpdate(constructor); @@ -38401,12 +38573,15 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_updateDeleteScheduledMessages extends Update { - public static final int constructor = 0x90866cee; + public static final int constructor = 0xf2a71983; + public int flags; public Peer peer; public ArrayList messages = new ArrayList<>(); + public ArrayList sent_messages = new ArrayList<>(); public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); peer = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); int magic = stream.readInt32(exception); if (magic != 0x1cb5c415) { @@ -38419,10 +38594,24 @@ public void readParams(AbstractSerializedData stream, boolean exception) { for (int a = 0; a < count; a++) { messages.add(stream.readInt32(exception)); } + if ((flags & 1) != 0) { + 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++) { + sent_messages.add(stream.readInt32(exception)); + } + } } public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); + stream.writeInt32(flags); peer.serializeToStream(stream); stream.writeInt32(0x1cb5c415); int count = messages.size(); @@ -38430,6 +38619,14 @@ public void serializeToStream(AbstractSerializedData stream) { for (int a = 0; a < count; a++) { stream.writeInt32(messages.get(a)); } + if ((flags & 1) != 0) { + stream.writeInt32(0x1cb5c415); + count = sent_messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt32(sent_messages.get(a)); + } + } } } @@ -43655,10 +43852,10 @@ public void serializeToStream(AbstractSerializedData stream) { } } - public static class TL_channels_reportSponsoredMessage extends TLObject { - public static final int constructor = 0xaf8ff6b9; + public static class TL_messages_reportSponsoredMessage extends TLObject { + public static final int constructor = 0x1af3dbb8; - public InputChannel channel; + public InputPeer peer; public byte[] random_id; public byte[] option; @@ -43668,7 +43865,7 @@ public TLObject deserializeResponse(AbstractSerializedData stream, int construct public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); - channel.serializeToStream(stream); + peer.serializeToStream(stream); stream.writeByteArray(random_id); stream.writeByteArray(option); } @@ -54167,6 +54364,7 @@ public static abstract class UserFull extends TLObject { public boolean phone_calls_available; public boolean phone_calls_private; public boolean can_pin_message; + public boolean bot_can_manage_emoji_status; public boolean has_scheduled; public boolean video_calls_available; public boolean voice_messages_forbidden; @@ -54177,6 +54375,7 @@ public static abstract class UserFull extends TLObject { public boolean contact_require_premium; public boolean read_dates_private; public boolean sponsored_enabled; + public boolean can_view_revenue; public User user; public String about; public TL_contacts_link_layer101 link; @@ -54296,6 +54495,8 @@ public void readParams(AbstractSerializedData stream, boolean exception) { read_dates_private = (flags & 1073741824) != 0; flags2 = stream.readInt32(exception); sponsored_enabled = (flags2 & 128) != 0; + can_view_revenue = (flags2 & 512) != 0; + bot_can_manage_emoji_status = (flags2 & 1024) != 0; id = stream.readInt64(exception); if ((flags & 2) != 0) { about = stream.readString(exception); @@ -54403,6 +54604,8 @@ public void serializeToStream(AbstractSerializedData stream) { flags = read_dates_private ? (flags | 1073741824) : (flags &~ 1073741824); stream.writeInt32(flags); flags2 = sponsored_enabled ? (flags2 | 128) : (flags2 &~ 128); + flags2 = can_view_revenue ? (flags2 | 512) : (flags2 &~ 512); + flags2 = bot_can_manage_emoji_status ? (flags2 | 1024) : (flags2 &~ 1024); stream.writeInt32(flags2); stream.writeInt64(id); if ((flags & 2) != 0) { @@ -66762,10 +66965,10 @@ public void serializeToStream(AbstractSerializedData stream) { } } - public static class TL_channels_viewSponsoredMessage extends TLObject { - public static final int constructor = 0xbeaedb94; + public static class TL_messages_viewSponsoredMessage extends TLObject { + public static final int constructor = 0x673ad8f1; - public InputChannel channel; + public InputPeer peer; public byte[] random_id; public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { @@ -66774,15 +66977,15 @@ public TLObject deserializeResponse(AbstractSerializedData stream, int construct public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); - channel.serializeToStream(stream); + peer.serializeToStream(stream); stream.writeByteArray(random_id); } } - public static class TL_channels_getSponsoredMessages extends TLObject { - public static final int constructor = 0xec210fbf; + public static class TL_messages_getSponsoredMessages extends TLObject { + public static final int constructor = 0x9bd2f439; - public InputChannel channel; + public InputPeer peer; public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { return messages_SponsoredMessages.TLdeserialize(stream, constructor, exception); @@ -66790,7 +66993,7 @@ public TLObject deserializeResponse(AbstractSerializedData stream, int construct public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); - channel.serializeToStream(stream); + peer.serializeToStream(stream); } } @@ -69174,6 +69377,7 @@ public static class Message extends TLObject { public boolean noforwards; public boolean invert_media; public boolean offline; + public boolean video_processing_pending; public TL_factCheck factcheck; public int send_state = 0; //custom public int fwd_msg_id = 0; //custom @@ -70050,6 +70254,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { invert_media = (flags & 134217728) != 0; flags2 = stream.readInt32(exception); offline = (flags2 & 2) != 0; + video_processing_pending = (flags2 & 16) != 0; id = stream.readInt32(exception); if ((flags & 256) != 0) { from_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); @@ -70171,6 +70376,7 @@ public void serializeToStream(AbstractSerializedData stream) { flags = invert_media ? (flags | 134217728) : (flags &~ 134217728); stream.writeInt32(flags); flags2 = offline ? (flags2 | 2) : (flags2 &~ 2); + flags2 = video_processing_pending ? (flags2 | 16) : (flags2 &~ 16); stream.writeInt32(flags2); stream.writeInt32(id); if ((flags & 256) != 0) { @@ -75101,6 +75307,7 @@ public static class TL_webViewResultUrl extends TLObject { public int flags; public boolean fullsize; + public boolean fullscreen; public long query_id; public String url; @@ -75120,6 +75327,7 @@ public static TL_webViewResultUrl TLdeserialize(AbstractSerializedData stream, i public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); fullsize = (flags & 2) != 0; + fullscreen = (flags & 4) != 0; if ((flags & 1) != 0) { query_id = stream.readInt64(exception); } @@ -75129,6 +75337,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); flags = fullsize ? flags | 2 : flags &~ 2; + flags = fullscreen ? flags | 4 : flags &~ 4; stream.writeInt32(flags); if ((flags & 1) != 0) { stream.writeInt64(query_id); @@ -75144,6 +75353,7 @@ public static class TL_messages_requestWebView extends TLObject { public boolean from_bot_menu; public boolean silent; public boolean compact; + public boolean fullscreen; public InputPeer peer; public InputUser bot; public String url; @@ -75162,6 +75372,7 @@ public void serializeToStream(AbstractSerializedData stream) { flags = from_bot_menu ? (flags | 16) : (flags &~ 16); flags = silent ? (flags | 32) : (flags &~ 32); flags = compact ? (flags | 128) : (flags &~ 128); + flags = fullscreen ? (flags | 256) : (flags &~ 256); stream.writeInt32(flags); peer.serializeToStream(stream); bot.serializeToStream(stream); @@ -75189,6 +75400,7 @@ public static class TL_messages_requestMainWebView extends TLObject { public int flags; public boolean compact; + public boolean fullscreen; public InputPeer peer; public InputUser bot; public String start_param; @@ -75202,6 +75414,7 @@ public TLObject deserializeResponse(AbstractSerializedData stream, int construct public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); flags = compact ? (flags | 128) : (flags &~ 128); + flags = fullscreen ? (flags | 256) : (flags &~ 256); stream.writeInt32(flags); peer.serializeToStream(stream); bot.serializeToStream(stream); @@ -75269,6 +75482,7 @@ public static class TL_messages_requestAppWebView extends TLObject { public int flags; public boolean write_allowed; public boolean compact; + public boolean fullscreen; public InputPeer peer; public InputBotApp app; public String start_param; @@ -75283,6 +75497,7 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); flags = write_allowed ? (flags | 1) : (flags &~ 1); flags = compact ? (flags | 128) : (flags &~ 128); + flags = fullscreen ? (flags | 256) : (flags &~ 256); stream.writeInt32(flags); peer.serializeToStream(stream); app.serializeToStream(stream); @@ -75303,6 +75518,7 @@ public static class TL_messages_requestSimpleWebView extends TLObject { public boolean from_switch_webview; public boolean from_side_menu; public boolean compact; + public boolean fullscreen; public InputUser bot; public String url; public String start_param; @@ -75318,6 +75534,7 @@ public void serializeToStream(AbstractSerializedData stream) { flags = from_switch_webview ? (flags | 2) : (flags &~ 2); flags = from_side_menu ? (flags | 4) : (flags &~ 4); flags = compact ? (flags | 128) : (flags &~ 128); + flags = fullscreen ? (flags | 256) : (flags &~ 256); stream.writeInt32(flags); bot.serializeToStream(stream); if ((flags & 8) != 0) { @@ -77414,13 +77631,13 @@ public void serializeToStream(AbstractSerializedData stream) { } } - public static class TL_channels_clickSponsoredMessage extends TLObject { - public static final int constructor = 0x1445d75; + public static class TL_messages_clickSponsoredMessage extends TLObject { + public static final int constructor = 0xf093465; public int flags; public boolean media; public boolean fullscreen; - public InputChannel channel; + public InputPeer peer; public byte[] random_id; public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { @@ -77432,7 +77649,7 @@ public void serializeToStream(AbstractSerializedData stream) { flags = media ? flags | 1 : flags &~ 1; flags = fullscreen ? flags | 2 : flags &~ 2; stream.writeInt32(flags); - channel.serializeToStream(stream); + peer.serializeToStream(stream); stream.writeByteArray(random_id); } } @@ -78086,7 +78303,7 @@ public void serializeToStream(AbstractSerializedData stream) { public static class TL_updatePaidReactionPrivacy extends Update { public static final int constructor = 0x51ca7aec; - + public boolean isPrivate; public void readParams(AbstractSerializedData stream, boolean exception) { @@ -78099,6 +78316,30 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_updateBotSubscriptionExpire extends Update { + public static final int constructor = 0xa8ae3eb1; + + public long user_id; + public String payload; + public int until_date; + public int qts; + + public void readParams(AbstractSerializedData stream, boolean exception) { + user_id = stream.readInt64(exception); + payload = stream.readString(exception); + until_date = stream.readInt32(exception); + qts = stream.readInt32(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(user_id); + stream.writeString(payload); + stream.writeInt32(until_date); + stream.writeInt32(qts); + } + } + public static class TL_updateSavedDialogPinned extends Update { public static final int constructor = 0xaeaf9e74; @@ -79954,9 +80195,39 @@ public void serializeToStream(AbstractSerializedData stream) { } } - public static class TL_messageActionStarGift extends MessageAction { + public static class TL_messageActionStarGift_layer192 extends TL_messageActionStarGift { public static final int constructor = 0x9bb3ef44; + @Override + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + name_hidden = (flags & 1) != 0; + saved = (flags & 4) != 0; + converted = (flags & 8) != 0; + gift = TL_stars.StarGift.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 2) != 0) { + message = TL_textWithEntities.TLdeserialize(stream, stream.readInt32(exception), exception); + } + convert_stars = stream.readInt64(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = name_hidden ? (flags | 1) : (flags &~ 1); + flags = saved ? (flags | 4) : (flags &~ 4); + flags = converted ? (flags | 8) : (flags &~ 8); + stream.writeInt32(flags); + gift.serializeToStream(stream); + if ((flags & 2) != 0) { + message.serializeToStream(stream); + } + stream.writeInt64(convert_stars); + } + } + + public static class TL_messageActionStarGift extends MessageAction { + public static final int constructor = 0x8557637; + public int flags; public boolean name_hidden; public boolean saved; @@ -79977,7 +80248,9 @@ public void readParams(AbstractSerializedData stream, boolean exception) { if ((flags & 2) != 0) { message = TL_textWithEntities.TLdeserialize(stream, stream.readInt32(exception), exception); } - convert_stars = stream.readInt64(exception); + if ((flags & 16) != 0) { + convert_stars = stream.readInt64(exception); + } } public void serializeToStream(AbstractSerializedData stream) { @@ -79990,7 +80263,9 @@ public void serializeToStream(AbstractSerializedData stream) { if ((flags & 2) != 0) { message.serializeToStream(stream); } - stream.writeInt64(convert_stars); + if ((flags & 16) != 0) { + stream.writeInt64(convert_stars); + } } } @@ -83768,5 +84043,104 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeString(provider_charge_id); } } + + public static class TL_messages_preparedInlineMessage extends TLObject { + public static final int constructor = 0xff57708d; + + public long query_id; + public BotInlineResult result; + public ArrayList peer_types = new ArrayList<>(); + public int cache_time; + public ArrayList users = new ArrayList<>(); + + public static TL_messages_preparedInlineMessage TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_messages_preparedInlineMessage.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_messages_preparedInlineMessage", constructor)); + } else { + return null; + } + } + TL_messages_preparedInlineMessage result = new TL_messages_preparedInlineMessage(); + result.readParams(stream, exception); + return result; + } + + @Override + public void readParams(AbstractSerializedData stream, boolean exception) { + query_id = stream.readInt64(exception); + result = BotInlineResult.TLdeserialize(stream, stream.readInt32(exception), exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + InlineQueryPeerType object = InlineQueryPeerType.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + peer_types.add(object); + } + cache_time = stream.readInt32(exception); + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + @Override + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(query_id); + result.serializeToStream(stream); + 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(cache_time); + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_messages_getPreparedInlineMessage extends TLObject { + public static final int constructor = 0x857ebdb8; + + public InputUser bot; + public String id; + + @Override + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_messages_preparedInlineMessage.TLdeserialize(stream, constructor, exception); + } + + @Override + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + bot.serializeToStream(stream); + stream.writeString(id); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_bots.java b/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_bots.java index b9fea938dfe..1f04ba2d65f 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_bots.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_bots.java @@ -1,5 +1,8 @@ package org.telegram.tgnet.tl; +import android.graphics.Path; + +import org.telegram.messenger.SvgHelper; import org.telegram.tgnet.AbstractSerializedData; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; @@ -352,6 +355,7 @@ public static abstract class BotInfo extends TLObject { public TLRPC.Document description_document; public boolean has_preview_medias; public String privacy_policy_url; + public botAppSettings app_settings; public static BotInfo TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { BotInfo result = null; @@ -374,6 +378,9 @@ public static BotInfo TLdeserialize(AbstractSerializedData stream, int construct case TL_botInfo_layer185.constructor: result = new TL_botInfo_layer185(); break; + case TL_botInfo_layer192.constructor: + result = new TL_botInfo_layer192(); + break; case TL_botInfo.constructor: result = new TL_botInfo(); break; @@ -513,6 +520,89 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_botInfo extends BotInfo { + public static final int constructor = 0x36607333; + + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + has_preview_medias = (flags & 64) != 0; + if ((flags & 1) != 0) { + user_id = stream.readInt64(exception); + } + if ((flags & 2) != 0) { + description = stream.readString(exception); + } + if ((flags & 16) != 0) { + description_photo = TLRPC.Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 32) != 0) { + description_document = TLRPC.Document.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 4) != 0) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TLRPC.TL_botCommand object = TLRPC.TL_botCommand.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + commands.add(object); + } + } + if ((flags & 8) != 0) { + menu_button = BotMenuButton.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 128) != 0) { + privacy_policy_url = stream.readString(exception); + } + if ((flags & 256) != 0) { + app_settings = botAppSettings.TLdeserialize(stream, stream.readInt32(exception), exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = has_preview_medias ? flags | 64 : flags &~ 64; + stream.writeInt32(flags); + if ((flags & 1) != 0) { + stream.writeInt64(user_id); + } + if ((flags & 2) != 0) { + stream.writeString(description); + } + if ((flags & 16) != 0) { + description_photo.serializeToStream(stream); + } + if ((flags & 32) != 0) { + description_document.serializeToStream(stream); + } + if ((flags & 4) != 0) { + stream.writeInt32(0x1cb5c415); + int count = commands.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + commands.get(a).serializeToStream(stream); + } + } + if ((flags & 8) != 0) { + menu_button.serializeToStream(stream); + } + if ((flags & 128) != 0) { + stream.writeString(privacy_policy_url); + } + if ((flags & 256) != 0) { + app_settings.serializeToStream(stream); + } + } + } + + public static class TL_botInfo_layer192 extends TL_botInfo { public static final int constructor = 0x82437e74; @@ -934,4 +1024,113 @@ public void serializeToStream(AbstractSerializedData stream) { } } } + + public static class botAppSettings extends TLObject { + public static final int constructor = 0xc99b1950; + + public int flags; + public byte[] placeholder_path; + public Path placeholder_svg_path; //custom + public int background_color; + public int background_dark_color; + public int header_color; + public int header_dark_color; + + public static botAppSettings TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (botAppSettings.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in botAppSettings", constructor)); + } else { + return null; + } + } + botAppSettings result = new botAppSettings(); + result.readParams(stream, exception); + return result; + } + + @Override + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + if ((flags & 1) != 0) { + placeholder_path = stream.readByteArray(exception); + placeholder_svg_path = SvgHelper.doPath(SvgHelper.decompress(placeholder_path)); + } + if ((flags & 2) != 0) { + background_color = stream.readInt32(exception); + } + if ((flags & 4) != 0) { + background_dark_color = stream.readInt32(exception); + } + if ((flags & 8) != 0) { + header_color = stream.readInt32(exception); + } + if ((flags & 16) != 0) { + header_dark_color = stream.readInt32(exception); + } + } + + @Override + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + if ((flags & 1) != 0) { + stream.writeByteArray(placeholder_path); + } + if ((flags & 2) != 0) { + stream.writeInt32(background_color); + } + if ((flags & 4) != 0) { + stream.writeInt32(background_dark_color); + } + if ((flags & 8) != 0) { + stream.writeInt32(header_color); + } + if ((flags & 16) != 0) { + stream.writeInt32(header_dark_color); + } + } + } + + public static class toggleUserEmojiStatusPermission extends TLObject { + public static final int constructor = 0x6de6392; + + public TLRPC.InputUser bot; + public boolean enabled; + + @Override + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TLRPC.Bool.TLdeserialize(stream, constructor, exception); + } + + @Override + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + bot.serializeToStream(stream); + stream.writeBool(enabled); + } + } + + public static class checkDownloadFileParams extends TLObject { + public static final int constructor = 0x50077589; + + public TLRPC.InputUser bot; + public String file_name; + public String url; + + @Override + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TLRPC.Bool.TLdeserialize(stream, constructor, exception); + } + + @Override + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + bot.serializeToStream(stream); + stream.writeString(file_name); + stream.writeString(url); + } + + } + } diff --git a/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_stars.java b/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_stars.java index fc32a640f25..de89c858ab9 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_stars.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_stars.java @@ -13,6 +13,7 @@ public static class StarGift extends TLObject { public int flags; public boolean limited; public boolean sold_out; + public boolean birthday; public long id; public TLRPC.Document sticker; public long stars; @@ -51,6 +52,7 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); flags = limited ? flags | 1 : flags &~ 1; flags = sold_out ? flags | 2 : flags &~ 2; + flags = birthday ? flags | 4 : flags &~ 4; stream.writeInt32(flags); stream.writeInt64(id); sticker.serializeToStream(stream); @@ -71,6 +73,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); limited = (flags & 1) != 0; sold_out = (flags & 2) != 0; + birthday = (flags & 4) != 0; id = stream.readInt64(exception); sticker = TLRPC.Document.TLdeserialize(stream, stream.readInt32(exception), exception); stars = stream.readInt64(exception); @@ -671,6 +674,9 @@ public static StarsTransactionPeer TLdeserialize(AbstractSerializedData stream, case TL_starsTransactionPeerAds.constructor: result = new TL_starsTransactionPeerAds(); break; + case TL_starsTransactionPeerAPI.constructor: + result = new TL_starsTransactionPeerAPI(); + break; } if (result == null && exception) { throw new RuntimeException(String.format("can't parse magic %x in StarsTransactionPeer", constructor)); @@ -755,6 +761,16 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_starsTransactionPeerAPI extends StarsTransactionPeer { + public static final int constructor = 0xf9677aad; + + public void readParams(AbstractSerializedData stream, boolean exception) {} + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static class StarsTransaction extends TLObject { public int flags; @@ -764,6 +780,7 @@ public static class StarsTransaction extends TLObject { public boolean gift; public boolean reaction; public boolean subscription; + public boolean floodskip; public String id; public long stars; public int date; @@ -779,6 +796,7 @@ public static class StarsTransaction extends TLObject { public int subscription_period; public int giveaway_post_id; public StarGift stargift; + public int floodskip_number; public TLRPC.Peer sent_by; //custom public TLRPC.Peer received_by; //custom @@ -801,6 +819,9 @@ public static StarsTransaction TLdeserialize(AbstractSerializedData stream, int case TL_starsTransaction_layer188.constructor: result = new TL_starsTransaction_layer188(); break; + case TL_starsTransaction_layer191.constructor: + result = new TL_starsTransaction_layer191(); + break; case TL_starsTransaction.constructor: result = new TL_starsTransaction(); break; @@ -909,6 +930,127 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_starsTransaction extends StarsTransaction { + public static final int constructor = 0x35d4f276; + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + refund = (flags & 8) != 0; + pending = (flags & 16) != 0; + failed = (flags & 64) != 0; + gift = (flags & 1024) != 0; + reaction = (flags & 2048) != 0; + subscription = (flags & 4096) != 0; + floodskip = (flags & 32768) != 0; + id = stream.readString(exception); + stars = stream.readInt64(exception); + date = stream.readInt32(exception); + peer = StarsTransactionPeer.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 1) != 0) { + title = stream.readString(exception); + } + if ((flags & 2) != 0) { + description = stream.readString(exception); + } + if ((flags & 4) != 0) { + photo = TLRPC.WebDocument.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 32) != 0) { + transaction_date = stream.readInt32(exception); + transaction_url = stream.readString(exception); + } + if ((flags & 128) != 0) { + bot_payload = stream.readByteArray(exception); + } + if ((flags & 256) != 0) { + msg_id = stream.readInt32(exception); + } + if ((flags & 512) != 0) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TLRPC.MessageMedia object = TLRPC.MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + extended_media.add(object); + } + } + if ((flags & 4096) != 0) { + subscription_period = stream.readInt32(exception); + } + if ((flags & 8192) != 0) { + giveaway_post_id = stream.readInt32(exception); + } + if ((flags & 16384) != 0) { + stargift = StarGift.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 32768) != 0) { + floodskip_number = stream.readInt32(exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = refund ? flags | 8 : flags &~ 8; + flags = pending ? flags | 16 : flags &~ 16; + flags = failed ? flags | 64 : flags &~ 64; + flags = gift ? flags | 1024 : flags &~ 1024; + flags = reaction ? flags | 2048 : flags &~ 2048; + flags = subscription ? flags | 4096 : flags &~ 4096; + flags = floodskip ? flags | 32768 : flags &~ 32768; + stream.writeInt32(flags); + stream.writeInt64(stars); + stream.writeInt32(date); + peer.serializeToStream(stream); + if ((flags & 1) != 0) { + stream.writeString(title); + } + if ((flags & 2) != 0) { + stream.writeString(description); + } + if ((flags & 4) != 0) { + photo.serializeToStream(stream); + } + if ((flags & 32) != 0) { + stream.writeInt32(transaction_date); + stream.writeString(transaction_url); + } + if ((flags & 128) != 0) { + stream.writeByteArray(bot_payload); + } + if ((flags & 256) != 0) { + stream.writeInt32(msg_id); + } + if ((flags & 512) != 0) { + stream.writeInt32(0x1cb5c415); + int count = extended_media.size(); + stream.writeInt32(count); + for (int i = 0; i < count; ++i) { + extended_media.get(i).serializeToStream(stream); + } + } + if ((flags & 4096) != 0) { + stream.writeInt32(subscription_period); + } + if ((flags & 8192) != 0) { + stream.writeInt32(giveaway_post_id); + } + if ((flags & 16384) != 0) { + stargift.serializeToStream(stream); + } + if ((flags & 32768) != 0) { + stream.writeInt32(floodskip_number); + } + } + } + + public static class TL_starsTransaction_layer191 extends TL_starsTransaction { public static final int constructor = 0xa9ee4c2; public void readParams(AbstractSerializedData stream, boolean exception) { @@ -1597,11 +1739,15 @@ public static class StarsSubscription extends TLObject { public boolean canceled; public boolean can_refulfill; public boolean missing_balance; + public boolean bot_canceled; public String id; public TLRPC.Peer peer; public int until_date; public TL_starsSubscriptionPricing pricing; public String chat_invite_hash; + public String title; + public TLRPC.WebDocument photo; + public String invoice_slug; public static StarsSubscription TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { StarsSubscription result = null; @@ -1609,6 +1755,9 @@ public static StarsSubscription TLdeserialize(AbstractSerializedData stream, int case TL_starsSubscription.constructor: result = new TL_starsSubscription(); break; + case TL_starsSubscription_layer193.constructor: + result = new TL_starsSubscription_layer193(); + break; case TL_starsSubscription_old.constructor: result = new TL_starsSubscription_old(); break; @@ -1625,6 +1774,61 @@ public static StarsSubscription TLdeserialize(AbstractSerializedData stream, int } public static class TL_starsSubscription extends StarsSubscription { + public static final int constructor = 0x2e6eab1a; + + @Override + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + canceled = (flags & 1) != 0; + can_refulfill = (flags & 2) != 0; + missing_balance = (flags & 4) != 0; + bot_canceled = (flags & 128) != 0; + id = stream.readString(exception); + peer = TLRPC.Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + until_date = stream.readInt32(exception); + pricing = TL_starsSubscriptionPricing.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 8) != 0) { + chat_invite_hash = stream.readString(exception); + } + if ((flags & 16) != 0) { + title = stream.readString(exception); + } + if ((flags & 32) != 0) { + photo = TLRPC.WebDocument.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 64) != 0) { + invoice_slug = stream.readString(exception); + } + } + + @Override + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = canceled ? (flags | 1) : (flags &~ 1); + flags = can_refulfill ? (flags | 2) : (flags &~ 2); + flags = missing_balance ? (flags | 4) : (flags &~ 4); + flags = bot_canceled ? (flags | 128) : (flags &~ 128); + stream.writeInt32(flags); + stream.writeString(id); + peer.serializeToStream(stream); + stream.writeInt32(until_date); + pricing.serializeToStream(stream); + if ((flags & 8) != 0) { + stream.writeString(chat_invite_hash); + } + if ((flags & 16) != 0) { + stream.writeString(title); + } + if ((flags & 32) != 0) { + photo.serializeToStream(stream); + } + if ((flags & 64) != 0) { + stream.writeString(invoice_slug); + } + } + } + + public static class TL_starsSubscription_layer193 extends StarsSubscription { public static final int constructor = 0x538ecf18; @Override diff --git a/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_stats.java b/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_stats.java index c612680b1ca..c2665c73fb2 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_stats.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_stats.java @@ -1009,11 +1009,11 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_getBroadcastRevenueStats extends TLObject { - public static final int constructor = 0x75dfb671; + public static final int constructor = 0xf788ee19; public int flags; public boolean dark; - public TLRPC.InputChannel channel; + public TLRPC.InputPeer peer; @Override public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { @@ -1025,14 +1025,14 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); flags = dark ? (flags | 1) : (flags & ~1); stream.writeInt32(flags); - channel.serializeToStream(stream); + peer.serializeToStream(stream); } } public static class TL_getBroadcastRevenueWithdrawalUrl extends TLObject { - public static final int constructor = 0x2a65ef73; + public static final int constructor = 0x9df4faad; - public TLRPC.InputChannel channel; + public TLRPC.InputPeer peer; public TLRPC.InputCheckPasswordSRP password; @Override @@ -1043,15 +1043,15 @@ public TLObject deserializeResponse(AbstractSerializedData stream, int construct @Override public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); - channel.serializeToStream(stream); + peer.serializeToStream(stream); password.serializeToStream(stream); } } public static class TL_getBroadcastRevenueTransactions extends TLObject { - public static final int constructor = 0x69280f; + public static final int constructor = 0x70990b6d; - public TLRPC.InputChannel channel; + public TLRPC.InputPeer peer; public int offset; public int limit; @@ -1063,7 +1063,7 @@ public TLObject deserializeResponse(AbstractSerializedData stream, int construct @Override public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); - channel.serializeToStream(stream); + peer.serializeToStream(stream); stream.writeInt32(offset); stream.writeInt32(limit); } diff --git a/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_stories.java b/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_stories.java index ec80c055166..fa5e8953f02 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_stories.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_stories.java @@ -1372,11 +1372,12 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_stories_searchPosts extends TLObject { - public static final int constructor = 0x6cea116a; + public static final int constructor = 0xd1810907; public int flags; public String hashtag; public MediaArea area; + public TLRPC.InputPeer peer; public String offset; public int limit; @@ -1395,6 +1396,9 @@ public void serializeToStream(AbstractSerializedData stream) { if ((flags & 2) != 0) { area.serializeToStream(stream); } + if ((flags & 4) != 0) { + peer.serializeToStream(stream); + } stream.writeString(offset); stream.writeInt32(limit); } 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 e5a1acb893a..cf2473033cb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java @@ -185,7 +185,10 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int usableViewHeight = rootView.getHeight() - (rect.top != 0 ? AndroidUtilities.statusBarHeight : 0) - AndroidUtilities.getViewInset(rootView); boolean isKeyboardVisible = usableViewHeight - (rect.bottom - rect.top) > 0; - int bottomTabsHeight = isKeyboardVisible ? 0 : getBottomTabsHeight(false); + if (bottomSheetTabs != null) { + bottomSheetTabs.updateCurrentAccount(); + } + final int bottomTabsHeight = isKeyboardVisible ? 0 : getBottomTabsHeight(false); for (int a = 0; a < count; a++) { View child = getChildAt(a); @@ -401,6 +404,7 @@ public boolean allowSwipe() { private DrawerLayoutContainer drawerLayoutContainer; private ActionBar currentActionBar; private BottomSheetTabs bottomSheetTabs; + private BottomSheetTabs.ClipTools bottomSheetTabsClip; private EmptyBaseFragment sheetFragment; public EmptyBaseFragment getSheetFragment() { @@ -516,14 +520,17 @@ public ActionBarLayout(Context context, boolean main) { public void setFragmentStack(List stack) { this.fragmentsStack = stack; + if (bottomSheetTabs != null) { + bottomSheetTabs.stopListening(this::invalidate, this::relayout); + AndroidUtilities.removeFromParent(bottomSheetTabs); + bottomSheetTabs = null; + } + LayoutParams layoutParams; if (main) { - if (bottomSheetTabs != null) { - AndroidUtilities.removeFromParent(bottomSheetTabs); - bottomSheetTabs = null; - } - bottomSheetTabs = new BottomSheetTabs(parentActivity, this); + bottomSheetTabsClip = new BottomSheetTabs.ClipTools(bottomSheetTabs); + bottomSheetTabs.listen(this::invalidate, this::relayout); layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp(68 + 8)); layoutParams.gravity = Gravity.BOTTOM | Gravity.FILL_HORIZONTAL; addView(bottomSheetTabs, layoutParams); @@ -565,7 +572,19 @@ public void setFragmentStack(List stack) { layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; sheetContainer.setLayoutParams(layoutParams); - + if (sheetFragment != null) { + sheetFragment.setParentLayout(this); + View fragmentView = sheetFragment.fragmentView; + if (fragmentView == null) { + fragmentView = sheetFragment.createView(parentActivity); + } + if (fragmentView.getParent() != sheetContainer) { + AndroidUtilities.removeFromParent(fragmentView); + sheetContainer.addView(fragmentView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + } + sheetFragment.onResume(); + sheetFragment.onBecomeFullyVisible(); + } for (BaseFragment fragment : fragmentsStack) { fragment.setParentLayout(this); @@ -863,8 +882,8 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { } final int restoreCount2 = canvas.save(); - if (child != bottomSheetTabs) { - clipBottomSheetTabs(canvas, withShadow); + if (child != bottomSheetTabs && bottomSheetTabsClip != null) { + bottomSheetTabsClip.clip(canvas, withShadow, isKeyboardVisible, getWidth(), (int) getY() + getHeight(), 1.0f); withShadow = false; } @@ -909,34 +928,6 @@ public void parentDraw(View parent, Canvas canvas) { } } - private final RectF clipRect = new RectF(); - private final float[] clipRadius = new float[8]; - private final Path clipPath = new Path(); - private final Paint clipShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - - public void clipBottomSheetTabs(Canvas canvas, boolean withShadow) { - if (bottomSheetTabs == null) - return; - final int bottomSheetHeight = isKeyboardVisible ? 0 : getBottomTabsHeight(true); - final int bottomRadius = Math.min(1, bottomSheetHeight / dp(60)) * dp(10); - if (bottomSheetHeight <= 0) - return; - - clipRadius[0] = clipRadius[1] = clipRadius[2] = clipRadius[3] = 0; // top - clipRadius[4] = clipRadius[5] = clipRadius[6] = clipRadius[7] = bottomRadius; // bottom - - clipPath.rewind(); - clipRect.set(0, 0, getWidth(), bottomSheetTabs.getY() + bottomSheetTabs.getHeight() - bottomSheetHeight); - clipPath.addRoundRect(clipRect, clipRadius, Path.Direction.CW); - - clipShadowPaint.setAlpha(0); - if (withShadow) { - clipShadowPaint.setShadowLayer(dp(2), 0, dp(1), 0x10000000); - canvas.drawPath(clipPath, clipShadowPaint); - } - canvas.clipPath(clipPath); - } - public void setOverrideWidthOffset(int overrideWidthOffset) { this.overrideWidthOffset = overrideWidthOffset; invalidate(); @@ -2965,57 +2956,18 @@ public void setNavigationBarColor(int color) { } } - private ValueAnimator bottomTabsAnimator; - public float bottomTabsProgress; - public int bottomTabsHeight; - - public void updateBottomTabsVisibility(boolean animated) { - if (bottomSheetTabs == null) { - return; - } - if (bottomTabsAnimator != null) { - ValueAnimator prev = bottomTabsAnimator; - bottomTabsAnimator = null; - prev.cancel(); - } - if (bottomTabsHeight == bottomSheetTabs.getExpandedHeight()) - return; - bottomTabsHeight = bottomSheetTabs.getExpandedHeight(); + public void relayout() { requestLayout(); containerView.requestLayout(); containerViewBack.requestLayout(); sheetContainer.requestLayout(); - if (animated) { - bottomTabsAnimator = ValueAnimator.ofFloat(bottomTabsProgress, bottomTabsHeight); - bottomTabsAnimator.addUpdateListener(anm -> { - bottomTabsProgress = (float) anm.getAnimatedValue(); - invalidate(); - }); - bottomTabsAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (bottomTabsAnimator == animation) { - bottomTabsProgress = bottomTabsHeight; - invalidate(); - } - } - }); - bottomTabsAnimator.setDuration(AdjustPanLayoutHelper.keyboardDuration); - bottomTabsAnimator.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); - bottomTabsAnimator.start(); - } else { - bottomTabsProgress = bottomTabsHeight; - } } @Override public int getBottomTabsHeight(boolean animated) { - if (!main) return 0; - if (animated) { - return (int) bottomTabsProgress; - } else { - return bottomTabsHeight; - } + if (main && bottomSheetTabs != null) + return bottomSheetTabs.getHeight(animated); + return 0; } } \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java index b06c0b0dee8..5caef90bbf6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java @@ -53,6 +53,7 @@ import android.widget.LinearLayout; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.core.graphics.ColorUtils; @@ -189,6 +190,7 @@ public interface ActionBarMenuItemDelegate { private Runnable showMenuRunnable; private int subMenuOpenSide; private int yOffset; + private int xOffset; private ActionBarMenuItemDelegate delegate; private ActionBarSubMenuItemDelegate subMenuDelegate; private boolean allowCloseAnimation = true; @@ -726,6 +728,10 @@ public void setMenuYOffset(int offset) { yOffset = offset; } + public void setMenuXOffset(int offset) { + xOffset = offset; + } + public void toggleSubMenu(View topView, View fromView) { if (popupWindow == null || !popupWindow.isShowing()) { layoutLazyItems(); @@ -1844,21 +1850,21 @@ private void updateOrShowPopup(boolean show, boolean update) { View parent = parentMenu.parentActionBar; if (subMenuOpenSide == 0) { if (show) { - popupWindow.showAsDropDown(parent, fromView.getLeft() + parentMenu.getLeft() + fromView.getMeasuredWidth() - popupWindow.getContentView().getMeasuredWidth() + (int) getTranslationX(), offsetY); + popupWindow.showAsDropDown(parent, fromView.getLeft() + parentMenu.getLeft() + fromView.getMeasuredWidth() - popupWindow.getContentView().getMeasuredWidth() + (int) getTranslationX() + xOffset, offsetY); } if (update) { - popupWindow.update(parent, fromView.getLeft() + parentMenu.getLeft() + fromView.getMeasuredWidth() - popupWindow.getContentView().getMeasuredWidth() + (int) getTranslationX(), offsetY, -1, -1); + popupWindow.update(parent, fromView.getLeft() + parentMenu.getLeft() + fromView.getMeasuredWidth() - popupWindow.getContentView().getMeasuredWidth() + (int) getTranslationX() + xOffset, offsetY, -1, -1); } } else { if (show) { if (forceSmoothKeyboard) { - popupWindow.showAtLocation(parent, Gravity.LEFT | Gravity.TOP, getLeft() - AndroidUtilities.dp(8) + (int) getTranslationX(), offsetY); + popupWindow.showAtLocation(parent, Gravity.LEFT | Gravity.TOP, getLeft() - AndroidUtilities.dp(8) + (int) getTranslationX() + xOffset, offsetY); } else { - popupWindow.showAsDropDown(parent, getLeft() - AndroidUtilities.dp(8) + (int) getTranslationX(), offsetY); + popupWindow.showAsDropDown(parent, getLeft() - AndroidUtilities.dp(8) + (int) getTranslationX() + xOffset, offsetY); } } if (update) { - popupWindow.update(parent, getLeft() - AndroidUtilities.dp(8) + (int) getTranslationX(), offsetY, -1, -1); + popupWindow.update(parent, getLeft() - AndroidUtilities.dp(8) + (int) getTranslationX() + xOffset, offsetY, -1, -1); } } } else { @@ -1866,25 +1872,25 @@ private void updateOrShowPopup(boolean show, boolean update) { if (getParent() != null) { View parent = (View) getParent(); if (show) { - popupWindow.showAsDropDown(parent, getLeft() + getMeasuredWidth() - popupWindow.getContentView().getMeasuredWidth() + additionalXOffset, offsetY); + popupWindow.showAsDropDown(parent, getLeft() + getMeasuredWidth() - popupWindow.getContentView().getMeasuredWidth() + additionalXOffset + xOffset, offsetY); } if (update) { - popupWindow.update(parent, getLeft() + getMeasuredWidth() - popupWindow.getContentView().getMeasuredWidth() + additionalXOffset, offsetY, -1, -1); + popupWindow.update(parent, getLeft() + getMeasuredWidth() - popupWindow.getContentView().getMeasuredWidth() + additionalXOffset + xOffset, offsetY, -1, -1); } } } else if (subMenuOpenSide == 1) { if (show) { - popupWindow.showAsDropDown(this, -AndroidUtilities.dp(8) + additionalXOffset, offsetY); + popupWindow.showAsDropDown(this, -AndroidUtilities.dp(8) + additionalXOffset + xOffset, offsetY); } if (update) { - popupWindow.update(this, -AndroidUtilities.dp(8) + additionalXOffset, offsetY, -1, -1); + popupWindow.update(this, -AndroidUtilities.dp(8) + additionalXOffset + xOffset, offsetY, -1, -1); } } else { if (show) { - popupWindow.showAsDropDown(this, getMeasuredWidth() - popupWindow.getContentView().getMeasuredWidth() + additionalXOffset, offsetY); + popupWindow.showAsDropDown(this, getMeasuredWidth() - popupWindow.getContentView().getMeasuredWidth() + additionalXOffset + xOffset, offsetY); } if (update) { - popupWindow.update(this, getMeasuredWidth() - popupWindow.getContentView().getMeasuredWidth() + additionalXOffset, offsetY, -1, -1); + popupWindow.update(this, getMeasuredWidth() - popupWindow.getContentView().getMeasuredWidth() + additionalXOffset + xOffset, offsetY, -1, -1); } } } @@ -2111,7 +2117,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @Override protected void onDraw(Canvas canvas) { if (reactionButton != null) { - reactionButton.draw(canvas, (getWidth() - dp(4) - reactionButton.width) / 2f, (getHeight() - reactionButton.height) / 2f, 1f, 1f, false); + reactionButton.draw(canvas, (getWidth() - dp(4) - reactionButton.width) / 2f, (getHeight() - reactionButton.height) / 2f, 1f, 1f, false, false, 0.0f); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuSlider.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuSlider.java index 3585e19d9f6..13fa1e86c2e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuSlider.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuSlider.java @@ -25,6 +25,7 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.TextUtils; +import android.util.Log; import android.util.Pair; import android.view.Gravity; import android.view.MotionEvent; @@ -37,6 +38,7 @@ import androidx.core.math.MathUtils; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.FileLog; import org.telegram.messenger.LiteMode; import org.telegram.messenger.LocaleController; import org.telegram.messenger.R; @@ -57,7 +59,8 @@ public class ActionBarMenuSlider extends FrameLayout { private float value = .5f; private Utilities.Callback2 onValueChange; - private AnimatedTextView.AnimatedTextDrawable textDrawable; + private AnimatedTextView.AnimatedTextDrawable leftTextDrawable; + private AnimatedTextView.AnimatedTextDrawable rightTextDrawable; private AnimatedFloat blurBitmapAlpha = new AnimatedFloat(1, this, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); private Bitmap blurBitmap; @@ -71,15 +74,21 @@ public class ActionBarMenuSlider extends FrameLayout { protected Theme.ResourcesProvider resourcesProvider; - private Paint shadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - private Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - private Paint blurPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - private Paint brightenBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - private Paint darkenBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - private Paint pseudoBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - private Paint fillPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint shadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint blurPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint brightenBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint darkenBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint pseudoBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint fillPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint stopPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private boolean backgroundDark; + private float[] stops; + public void setStops(float[] stops) { + this.stops = stops; + } + private boolean blurIsInChat = true; private LinearGradient pseudoBlurGradient; @@ -101,19 +110,33 @@ public ActionBarMenuSlider(Context context, Theme.ResourcesProvider resourcesPro this.resourcesProvider = resourcesProvider; setWillNotDraw(false); - textDrawable = new AnimatedTextView.AnimatedTextDrawable(false, true, true) { + leftTextDrawable = new AnimatedTextView.AnimatedTextDrawable(false, true, true) { + @Override + public void invalidateSelf() { + ActionBarMenuSlider.this.invalidate(); + } + }; + leftTextDrawable.setCallback(this); + leftTextDrawable.setTypeface(AndroidUtilities.bold()); + leftTextDrawable.setAnimationProperties(.3f, 0, 165, CubicBezierInterpolator.EASE_OUT_QUINT); + leftTextDrawable.setTextSize(AndroidUtilities.dpf2(14)); + leftTextDrawable.getPaint().setStyle(Paint.Style.FILL_AND_STROKE); + leftTextDrawable.getPaint().setStrokeWidth(AndroidUtilities.dpf2(.3f)); + leftTextDrawable.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + + rightTextDrawable = new AnimatedTextView.AnimatedTextDrawable(false, true, true) { @Override public void invalidateSelf() { ActionBarMenuSlider.this.invalidate(); } }; - textDrawable.setCallback(this); - textDrawable.setTypeface(AndroidUtilities.bold()); - textDrawable.setAnimationProperties(.3f, 0, 165, CubicBezierInterpolator.EASE_OUT_QUINT); - textDrawable.setTextSize(AndroidUtilities.dpf2(14)); - textDrawable.getPaint().setStyle(Paint.Style.FILL_AND_STROKE); - textDrawable.getPaint().setStrokeWidth(AndroidUtilities.dpf2(.3f)); - textDrawable.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + rightTextDrawable.setCallback(this); + rightTextDrawable.setTypeface(AndroidUtilities.bold()); + rightTextDrawable.setAnimationProperties(.3f, 0, 165, CubicBezierInterpolator.EASE_OUT_QUINT); + rightTextDrawable.setTextSize(AndroidUtilities.dpf2(14)); + rightTextDrawable.getPaint().setStyle(Paint.Style.FILL_AND_STROKE); + rightTextDrawable.getPaint().setStrokeWidth(AndroidUtilities.dpf2(.3f)); + rightTextDrawable.setGravity(LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT); shadowPaint.setColor(Color.TRANSPARENT); shadowPaint.setShadowLayer(dpf2(1.33f), 0, dpf2(.33f), 0x3f000000); @@ -125,9 +148,11 @@ public void invalidateSelf() { backgroundPaint.setColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground, resourcesProvider)); backgroundDark = AndroidUtilities.computePerceivedBrightness(backgroundPaint.getColor()) <= 0.721f; - textDrawable.setTextColor(backgroundDark ? 0xffffffff : 0xff000000); + leftTextDrawable.setTextColor(backgroundDark ? 0xffffffff : 0xff000000); + rightTextDrawable.setTextColor(backgroundDark ? 0xffffffff : 0xff000000); darkenBlurPaint.setColor(Theme.multAlpha(0xff000000, .025f)); brightenBlurPaint.setColor(Theme.multAlpha(0xffffffff, .35f)); + stopPaint.setColor(Theme.multAlpha(0xFFFFFFFF, .20f)); } public float getValue() { @@ -167,25 +192,36 @@ public void onAnimationEnd(Animator animation) { valueAnimator.start(); } - String stringValue = getStringValue(value); - if (stringValue != null && !TextUtils.equals(textDrawable.getText(), stringValue)) { - textDrawable.cancelAnimation(); - textDrawable.setText(stringValue, true); + String stringValue = getLeftStringValue(value); + if (stringValue != null && !TextUtils.equals(leftTextDrawable.getText(), stringValue)) { + leftTextDrawable.cancelAnimation(); + leftTextDrawable.setText(stringValue, true); } - fillPaint.setColor(getColorValue(value)); + stringValue = getRightStringValue(value); + if (stringValue != null && !TextUtils.equals(rightTextDrawable.getText(), stringValue)) { + rightTextDrawable.cancelAnimation(); + rightTextDrawable.setText(stringValue, true); + } + fillPaint.setColor(getColorValue(value));; } public void setBackgroundColor(int color) { backgroundPaint.setColor(color); backgroundDark = AndroidUtilities.computePerceivedBrightness(backgroundPaint.getColor()) <= 0.721f; - textDrawable.setTextColor(backgroundDark ? 0xffffffff : 0xff000000); + leftTextDrawable.setTextColor(backgroundDark ? 0xffffffff : 0xff000000); + rightTextDrawable.setTextColor(backgroundDark ? 0xffffffff : 0xff000000); } public void setTextColor(int color) { - textDrawable.setTextColor(color); + leftTextDrawable.setTextColor(color); + rightTextDrawable.setTextColor(color); + } + + protected String getLeftStringValue(float value) { + return null; } - protected String getStringValue(float value) { + protected String getRightStringValue(float value) { return null; } @@ -325,6 +361,7 @@ protected void onDraw(Canvas canvas) { } else { canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(roundRadiusDp), dp(roundRadiusDp), backgroundPaint); } + drawStops(canvas); if (!backgroundDark) { drawText(canvas, false); @@ -335,6 +372,7 @@ protected void onDraw(Canvas canvas) { canvas.clipRect(getPaddingLeft(), getPaddingTop(), getPaddingLeft() + (getWidth() - getPaddingLeft() - getPaddingRight()) * value, getHeight() - getPaddingBottom()); } canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(roundRadiusDp), dp(roundRadiusDp), fillPaint); + drawStops(canvas); if (!backgroundDark) { drawText(canvas, true); @@ -349,12 +387,30 @@ protected void onDraw(Canvas canvas) { } } + private void drawStops(Canvas canvas) { + if (stops == null) return; + for (int i = 0; i < stops.length; ++i) { + float stop = stops[i]; + canvas.drawRect( + AndroidUtilities.rectTmp.left + AndroidUtilities.rectTmp.width() * stop, + AndroidUtilities.rectTmp.top, + AndroidUtilities.rectTmp.left + AndroidUtilities.rectTmp.width() * stop + dp(0.66f), + AndroidUtilities.rectTmp.bottom, + stopPaint + ); + } + } + private ColorFilter whiteColorFilter; private void drawText(Canvas canvas, boolean white) { - textDrawable.setColorFilter(white ? (whiteColorFilter == null ? whiteColorFilter = new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN) : whiteColorFilter) : null); - textDrawable.setBounds(getPaddingLeft() + dp(20), getMeasuredHeight() / 2, getMeasuredWidth() - getPaddingRight() - dp(20), getMeasuredHeight() / 2); - textDrawable.draw(canvas); + leftTextDrawable.setColorFilter(white ? (whiteColorFilter == null ? whiteColorFilter = new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN) : whiteColorFilter) : null); + leftTextDrawable.setBounds(getPaddingLeft() + dp(20), getMeasuredHeight() / 2, getMeasuredWidth() - getPaddingRight() - dp(20), getMeasuredHeight() / 2); + leftTextDrawable.draw(canvas); + + rightTextDrawable.setColorFilter(white ? (whiteColorFilter == null ? whiteColorFilter = new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN) : whiteColorFilter) : null); + rightTextDrawable.setBounds(getPaddingLeft() + dp(20), getMeasuredHeight() / 2, getMeasuredWidth() - getPaddingRight() - dp(20), getMeasuredHeight() / 2); + rightTextDrawable.draw(canvas); } private Pair getBitmapGradientColors(Bitmap bitmap) { @@ -436,14 +492,30 @@ public boolean onTouchEvent(MotionEvent event) { if (action == MotionEvent.ACTION_UP) { dragging = false; if (System.currentTimeMillis() - tapStart < ViewConfiguration.getTapTimeout()) { - final float value = (x - getPaddingLeft()) / (getWidth() - getPaddingLeft() - getPaddingRight()); + float value = (x - getPaddingLeft()) / (getWidth() - getPaddingLeft() - getPaddingRight()); + if (stops != null) { + for (int i = 0; i < stops.length; ++i) { + if (Math.abs(value - stops[i]) < 0.1f) { + value = stops[i]; + break; + } + } + } if (onValueChange != null) { onValueChange.run(value, true); } return true; } } - final float value = fromValue + (x - fromX) / Math.max(1, getWidth() - getPaddingLeft() - getPaddingRight()); + float value = fromValue + (x - fromX) / Math.max(1, getWidth() - getPaddingLeft() - getPaddingRight()); + if (stops != null) { + for (int i = 0; i < stops.length; ++i) { + if (Math.abs(value - stops[i]) < 0.05f) { + value = stops[i]; + break; + } + } + } updateValue(value, !dragging); } @@ -455,7 +527,7 @@ public static class SpeedSlider extends ActionBarMenuSlider { private final SeekBarAccessibilityDelegate seekBarAccessibilityDelegate; public static final float MIN_SPEED = 0.2f; - public static final float MAX_SPEED = 2.5f; + public static final float MAX_SPEED = 3.0f; public SpeedSlider(Context context, Theme.ResourcesProvider resourcesProvider) { super(context, resourcesProvider); @@ -497,6 +569,11 @@ public CharSequence getContentDescription(View host) { }); } + String label = null; + public void setLabel(String s) { + label = s; + } + @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); @@ -521,34 +598,32 @@ public void setSpeed(float speed, boolean animated) { } @Override - protected String getStringValue(float value) { + protected String getLeftStringValue(float value) { + if (label != null) return label; + return SpeedIconDrawable.formatNumber(MIN_SPEED + value * (MAX_SPEED - MIN_SPEED)) + "x"; + } + + @Override + protected String getRightStringValue(float value) { + if (label == null) return null; return SpeedIconDrawable.formatNumber(MIN_SPEED + value * (MAX_SPEED - MIN_SPEED)) + "x"; } @Override protected int getColorValue(float value) { final float speed = MIN_SPEED + value * (MAX_SPEED - MIN_SPEED); -// if (speed <= 0.3f) { -// return Theme.getColor(Theme.key_color_red, resourcesProvider); -// } else if (speed <= 0.5f) { -// return ColorUtils.blendARGB( -// Theme.getColor(Theme.key_color_red, resourcesProvider), -// Theme.getColor(Theme.key_color_yellow, resourcesProvider), -// (speed - 0.3f) / (0.5f - 0.3f) -// ); -// } else if (speed <= 1f) { -// return ColorUtils.blendARGB( -// Theme.getColor(Theme.key_color_yellow, resourcesProvider), -// Theme.getColor(Theme.key_color_lightblue, resourcesProvider), -// MathUtils.clamp((speed - 0.5f) / (1f - 0.5f), 0, 1) -// ); -// } else { - return ColorUtils.blendARGB( - Theme.getColor(Theme.key_color_lightblue, resourcesProvider), - Theme.getColor(Theme.key_color_blue, resourcesProvider), - MathUtils.clamp((speed - 1f) / (2f - 1f), 0, 1) - ); -// } + return ColorUtils.blendARGB( + Theme.getColor(Theme.key_color_lightblue, resourcesProvider), + Theme.getColor(Theme.key_color_blue, resourcesProvider), + MathUtils.clamp((speed - 1f) / (2f - 1f), 0, 1) + ); + } + + @Override + public void setStops(float[] stops) { + for (int i = 0; i < stops.length; ++i) + stops[i] = (stops[i] - MIN_SPEED) / (MAX_SPEED - MIN_SPEED); + super.setStops(stops); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuSubItem.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuSubItem.java index fb6bab0a5c6..7a74e30d93d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuSubItem.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuSubItem.java @@ -2,6 +2,9 @@ import static org.telegram.messenger.AndroidUtilities.dp; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.content.Context; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; @@ -15,10 +18,13 @@ import android.widget.ImageView; import android.widget.TextView; +import androidx.core.graphics.ColorUtils; + import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.LocaleController; import org.telegram.ui.Components.AnimatedTextView; import org.telegram.ui.Components.CheckBox2; +import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RLottieImageView; @@ -189,13 +195,16 @@ public void setTextAndIcon(CharSequence text, int icon, Drawable iconDrawable) { textView.setText(text); if (icon != 0 || iconDrawable != null || checkView != null) { if (iconDrawable != null) { + iconResId = 0; imageView.setImageDrawable(iconDrawable); } else { + iconResId = icon; imageView.setImageResource(icon); } imageView.setVisibility(VISIBLE); textView.setPadding(checkViewLeft ? (checkView != null ? dp(43) : 0) : dp(icon != 0 || iconDrawable != null ? 43 : 0), 0, checkViewLeft ? dp(icon != 0 || iconDrawable != null ? 43 : 0) : (checkView != null ? dp(43) : 0), 0); } else { + iconResId = 0; imageView.setVisibility(INVISIBLE); textView.setPadding(0, 0, 0, 0); } @@ -219,15 +228,47 @@ public void setIconColor(int iconColor) { } } + private ValueAnimator enabledAnimator; + private boolean enabled; + public void setEnabledByColor(boolean enabled, int colorDisabled, int colorEnabled) { + if (enabledAnimator != null) { + enabledAnimator.cancel(); + } + enabledAnimator = ValueAnimator.ofFloat(this.enabled ? 1.0f : 0.0f, enabled ? 1.0f : 0.0f); + this.enabled = enabled; + enabledAnimator.addUpdateListener(anm -> { + final float t = (float) anm.getAnimatedValue(); + setTextColor(ColorUtils.blendARGB(colorDisabled, colorEnabled, t)); + setIconColor(ColorUtils.blendARGB(colorDisabled, colorEnabled, t)); + }); + enabledAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + final float t = enabled ? 1.0f : 0.0f; + setTextColor(ColorUtils.blendARGB(colorDisabled, colorEnabled, t)); + setIconColor(ColorUtils.blendARGB(colorDisabled, colorEnabled, t)); + } + }); + enabledAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + enabledAnimator.start(); + } + + private int iconResId; + public int getIconResId() { + return iconResId; + } + public void setIcon(int resId) { - imageView.setImageResource(resId); + imageView.setImageResource(iconResId = resId); } public void setIcon(Drawable drawable) { + iconResId = 0; imageView.setImageDrawable(drawable); } public void setAnimatedIcon(int resId) { + iconResId = 0; imageView.setAnimation(resId, 24, 24); } @@ -247,7 +288,7 @@ public void setSubtextColor(int color) { } } - public void setSubtext(String text) { + public void setSubtext(CharSequence text) { if (subtextView == null) { subtextView = new TextView(getContext()); subtextView.setLines(1); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AdjustPanLayoutHelper.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AdjustPanLayoutHelper.java index 2565ab4ff34..a96fa0d77ad 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AdjustPanLayoutHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AdjustPanLayoutHelper.java @@ -159,6 +159,7 @@ public void onAnimationEnd(Animator animation) { } } + public boolean showingKeyboard; public void startTransition(int previousHeight, int contentHeight, boolean isKeyboardVisible) { if (animator != null) { animator.cancel(); @@ -177,7 +178,7 @@ public void startTransition(int previousHeight, int contentHeight, boolean isKey if (LaunchActivity.instance != null && LaunchActivity.instance.getBottomSheetTabs() != null) { bottomTabsHeight += LaunchActivity.instance.getBottomSheetTabs().getExpandedHeight(); } - setViewHeight(Math.max(previousHeight, contentHeight + additionalContentHeight + bottomTabsHeight)); + if (applyTranslation()) setViewHeight(Math.max(previousHeight, contentHeight + additionalContentHeight + bottomTabsHeight)); resizableView.requestLayout(); onTransitionStart(isKeyboardVisible, previousHeight, contentHeight); @@ -186,15 +187,16 @@ public void startTransition(int previousHeight, int contentHeight, boolean isKey keyboardSize = Math.abs(dy); animationInProgress = true; + showingKeyboard = contentHeight <= previousHeight; if (contentHeight > previousHeight) { dy -= startOffset; - parent.setTranslationY(-dy); + if (applyTranslation()) parent.setTranslationY(-dy); onPanTranslationUpdate(dy, 1f, isKeyboardVisible); from = -dy; to = -bottomTabsHeight; inverse = true; } else { - parent.setTranslationY(previousStartOffset); + if (applyTranslation()) parent.setTranslationY(previousStartOffset); onPanTranslationUpdate(-previousStartOffset, 0f, isKeyboardVisible); to = -previousStartOffset; from = dy; @@ -209,7 +211,7 @@ public void updateTransition(float t) { t = 1f - t; } float y = (int) (from * t + to * (1f - t)); - parent.setTranslationY(y); + if (applyTranslation()) parent.setTranslationY(y); onPanTranslationUpdate(-y, t, isKeyboardVisible); } @@ -225,7 +227,7 @@ public void stopTransition() { viewsToHeightSet.clear(); resizableView.requestLayout(); onPanTranslationUpdate(0, isKeyboardVisible ? 1f : 0f, isKeyboardVisible); - parent.setTranslationY(0); + if (applyTranslation()) parent.setTranslationY(0); onTransitionEnd(); } public void stopTransition(float t, boolean isKeyboardVisible) { @@ -239,7 +241,7 @@ public void stopTransition(float t, boolean isKeyboardVisible) { viewsToHeightSet.clear(); resizableView.requestLayout(); onPanTranslationUpdate(0, t, this.isKeyboardVisible = isKeyboardVisible); - parent.setTranslationY(0); + if (applyTranslation()) parent.setTranslationY(0); onTransitionEnd(); } @@ -351,6 +353,9 @@ public void ignoreOnce() { protected boolean heightAnimationEnabled() { return true; } + protected boolean applyTranslation() { + return true; + } public void OnPanTranslationUpdate(float y, float progress, boolean keyboardVisible) { onPanTranslationUpdate(y, progress, keyboardVisible); 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 0a44bde40d6..f9a2a97625d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AlertDialog.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AlertDialog.java @@ -65,6 +65,7 @@ import org.telegram.messenger.Utilities; import org.telegram.ui.Components.AnimatedEmojiDrawable; import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.AttachableDrawable; import org.telegram.ui.Components.EffectsTextView; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LineProgressView; @@ -636,6 +637,20 @@ protected View inflateContent(boolean setContent) { topImageView = new RLottieImageView(getContext()); if (topDrawable != null) { topImageView.setImageDrawable(topDrawable); + if (topDrawable instanceof AttachableDrawable) { + final AttachableDrawable d = (AttachableDrawable) topDrawable; + topImageView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(@NonNull View v) { + d.onAttachedToWindow(null); + } + @Override + public void onViewDetachedFromWindow(@NonNull View v) { + d.onDetachedFromWindow(null); + } + }); + d.setParent(topImageView); + } } else if (topResId != 0) { topImageView.setImageResource(topResId); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java index 95ccde75e6f..fb5533b8951 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java @@ -157,6 +157,16 @@ public boolean hasShownSheet() { return false; } + public boolean hasShownFullyVisibleSheet() { + if (!hasSheet()) return false; + for (int i = sheetsStack.size() - 1; i >= 0; --i) { + if (sheetsStack.get(i).isShown() && sheetsStack.get(i).isFullyVisible()) { + return true; + } + } + return false; + } + public static boolean hasSheets(BaseFragment fragment) { if (fragment == null) return false; if (fragment.hasShownSheet()) return true; @@ -165,6 +175,14 @@ public static boolean hasSheets(BaseFragment fragment) { return sheetFragment != null && sheetFragment.hasShownSheet(); } + public static boolean hasFullyVisibleSheets(BaseFragment fragment) { + if (fragment == null) return false; + if (fragment.hasShownFullyVisibleSheet()) return true; + if (!(fragment.getParentLayout() instanceof ActionBarLayout)) return false; + final BaseFragment sheetFragment = ((ActionBarLayout) fragment.getParentLayout()).getSheetFragment(false); + return sheetFragment != null && sheetFragment.hasShownFullyVisibleSheet(); + } + public void clearSheets() { if (sheetsStack == null || sheetsStack.isEmpty()) return; @@ -1297,13 +1315,6 @@ public ArticleViewer createArticleViewer(boolean forceRecreate) { return articleViewer; } - public BotWebViewAttachedSheet createBotViewer() { - BotWebViewAttachedSheet botViewer = new BotWebViewAttachedSheet(this); - addSheet(botViewer); - BottomSheetTabDialog.checkSheet(botViewer); - return botViewer; - } - public void onBottomSheetCreated() { } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabDialog.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabDialog.java index f9da58e376f..4acbb629eb6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabDialog.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabDialog.java @@ -3,6 +3,7 @@ import android.app.Dialog; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Paint; import android.graphics.RectF; import android.os.Build; import android.os.Bundle; @@ -16,6 +17,7 @@ import android.view.WindowManager; import android.widget.FrameLayout; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.telegram.messenger.AndroidUtilities; @@ -31,7 +33,7 @@ public class BottomSheetTabDialog extends Dialog { public static BottomSheetTabsOverlay.Sheet checkSheet(BottomSheetTabsOverlay.Sheet sheet) { BaseFragment fragment = LaunchActivity.getSafeLastFragment(); if (fragment == null) return sheet; - if (AndroidUtilities.isTablet() || AndroidUtilities.hasDialogOnTop(fragment)) { + if (AndroidUtilities.isTablet() || sheet.hadDialog() || AndroidUtilities.hasDialogOnTop(fragment)) { final BottomSheetTabDialog dialog = new BottomSheetTabDialog(sheet); if (sheet.setDialog(dialog)) { dialog.windowView.putView(); @@ -45,6 +47,8 @@ public static BottomSheetTabsOverlay.Sheet checkSheet(BottomSheetTabsOverlay.She public final BottomSheetTabsOverlay.SheetView sheetView; public final WindowView windowView; + public final View navigationBar; + public final Paint navigationBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); public BottomSheetTabDialog(BottomSheetTabsOverlay.Sheet sheet) { super(sheet.getWindowView().getContext(), R.style.TransparentDialog); @@ -52,7 +56,22 @@ public BottomSheetTabDialog(BottomSheetTabsOverlay.Sheet sheet) { this.sheet = sheet; this.sheetView = sheet.getWindowView(); + navigationBar = new View(getContext()) { + @Override + protected void dispatchDraw(@NonNull Canvas canvas) { + canvas.drawRect(0,0,getWidth(),getHeight(),navigationBarPaint); + } + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), AndroidUtilities.navigationBarHeight); + setTranslationY(AndroidUtilities.navigationBarHeight); + } + }; + navigationBarPaint.setColor(Theme.getColor(Theme.key_windowBackgroundGray)); + setContentView(windowView = new WindowView(sheetView), new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + windowView.addView(navigationBar, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM)); + windowView.setClipToPadding(false); } @Override @@ -99,9 +118,12 @@ protected void onCreate(Bundle savedInstanceState) { } public void updateNavigationBarColor() { - final int color = sheet.getNavigationBarColor(0); + final int color = sheet.getNavigationBarColor(Theme.getColor(Theme.key_windowBackgroundGray)); + navigationBarPaint.setColor(color); + navigationBar.invalidate(); AndroidUtilities.setNavigationBarColor(getWindow(), color); AndroidUtilities.setLightNavigationBar(getWindow(), AndroidUtilities.computePerceivedBrightness(color) >= .721f); + LaunchActivity.instance.checkSystemBarColors(true, true, true, false); } public static class WindowView extends FrameLayout implements BottomSheetTabsOverlay.SheetView { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabs.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabs.java index d1212bf7d25..5c8e6b346bf 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabs.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabs.java @@ -4,6 +4,9 @@ import static org.telegram.messenger.AndroidUtilities.lerp; import static org.telegram.messenger.LocaleController.getString; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -46,6 +49,7 @@ import org.telegram.ui.Components.Text; import org.telegram.ui.LaunchActivity; import org.telegram.ui.bots.BotButtons; +import org.telegram.ui.bots.BotSensors; import org.telegram.ui.bots.BotWebViewAttachedSheet; import org.telegram.ui.web.BotWebViewContainer; import org.telegram.ui.bots.BotWebViewSheet; @@ -53,6 +57,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; public class BottomSheetTabs extends FrameLayout { @@ -68,25 +73,8 @@ public BottomSheetTabs(Context context, ActionBarLayout actionBarLayout) { setNavigationBarColor(Theme.getColor(Theme.key_windowBackgroundGray)); - setOnClickListener(v -> { - final ArrayList tabs = getTabs(); - - final int count = tabs.size(); - if (count == 0) return; - WebTabData lastTab = tabs.get(tabs.size() - 1); - BottomSheetTabsOverlay overlay = LaunchActivity.instance == null ? null : LaunchActivity.instance.getBottomSheetTabsOverlay(); - if (overlay != null) { - overlay.stopAnimations(); - } - - if (count == 1 || overlay == null) { - openTab(lastTab); - } else { - overlay.openTabsView(); - } - }); - updateMultipleTitle(); + updateVisibility(false); } public void openTab(WebTabData tab) { @@ -122,23 +110,23 @@ public void openTab(WebTabData tab) { if (fragment.getContext() == null || fragment.getParentActivity() == null) { return; } - if (AndroidUtilities.isTablet() && !tab.isWeb) { +// if (AndroidUtilities.isTablet() && !tab.isWeb || true) { BotWebViewSheet sheet = new BotWebViewSheet(fragment.getContext(), fragment.getResourceProvider()); sheet.setParentActivity(fragment.getParentActivity()); if (sheet.restoreState(fragment, tab)) { removeTab(tab, false); sheet.show(); } - } else { - BaseFragment sheetFragment = actionBarLayout.getSheetFragment(); - if (sheetFragment == null) return; - BotWebViewAttachedSheet webViewSheet = sheetFragment.createBotViewer(); - webViewSheet.setParentActivity(fragment.getParentActivity()); - if (webViewSheet.restoreState(fragment, tab)) { - removeTab(tab, false); - webViewSheet.show(closed); - } - } +// } else { +// BaseFragment sheetFragment = actionBarLayout.getSheetFragment(); +// if (sheetFragment == null) return; +// BotWebViewAttachedSheet webViewSheet = sheetFragment.createBotViewer(); +// webViewSheet.setParentActivity(fragment.getParentActivity()); +// if (webViewSheet.restoreState(fragment, tab)) { +// removeTab(tab, false); +// webViewSheet.show(closed); +// } +// } }; open.run(lastFragment); if (tab.needsContext && (!(lastFragment instanceof ChatActivity) || ((ChatActivity) lastFragment).getDialogId() != tab.props.botId)) { @@ -272,8 +260,8 @@ public void setNavigationBarColor(int color, boolean animated) { } public int currentAccount = UserConfig.selectedAccount; - public final HashMap> tabs = new HashMap<>(); - public final HashMap> tabDrawables = new HashMap<>(); + public static final HashMap> tabs = new HashMap<>(); + public static final HashMap> tabDrawables = new HashMap<>(); public void updateCurrentAccount() { setCurrentAccount(UserConfig.selectedAccount); @@ -283,7 +271,7 @@ public void setCurrentAccount(int account) { if (currentAccount != account) { currentAccount = account; - actionBarLayout.updateBottomTabsVisibility(false); + updateVisibility(false); invalidate(); } } @@ -312,14 +300,14 @@ public ArrayList getTabDrawables() { } public ArrayList getTabs(int currentAccount) { - ArrayList tabs = this.tabs.get(currentAccount); - if (tabs == null) this.tabs.put(currentAccount, tabs = new ArrayList<>()); + ArrayList tabs = BottomSheetTabs.tabs.get(currentAccount); + if (tabs == null) BottomSheetTabs.tabs.put(currentAccount, tabs = new ArrayList<>()); return tabs; } public ArrayList getTabDrawables(int currentAccount) { - ArrayList tabDrawables = this.tabDrawables.get(currentAccount); - if (tabDrawables == null) this.tabDrawables.put(currentAccount, tabDrawables = new ArrayList<>()); + ArrayList tabDrawables = BottomSheetTabs.tabDrawables.get(currentAccount); + if (tabDrawables == null) BottomSheetTabs.tabDrawables.put(currentAccount, tabDrawables = new ArrayList<>()); return tabDrawables; } @@ -354,7 +342,7 @@ public TabDrawable pushTab(WebTabData tab) { } updateMultipleTitle(); - actionBarLayout.updateBottomTabsVisibility(true); + updateVisibility(true); invalidate(); return tabDrawable; @@ -416,7 +404,7 @@ public boolean removeAll() { drawable.index = -1; } updateMultipleTitle(); - actionBarLayout.updateBottomTabsVisibility(true); + updateVisibility(true); invalidate(); return tabs.isEmpty(); } @@ -512,33 +500,41 @@ public boolean removeTab(int currentAccount, WebTabData tab, boolean destroy) { } invalidate(); }, 320); - actionBarLayout.updateBottomTabsVisibility(true); + updateVisibility(true); invalidate(); return tabs.isEmpty(); } private boolean closeRippleHit; + private boolean hit; + @Override public boolean onTouchEvent(MotionEvent event) { + return touchEvent(event.getAction(), event.getX(), event.getY()) || super.onTouchEvent(event); + } + + public boolean touchEvent(int action, float x, float y) { final ArrayList tabs = getTabs(); final ArrayList tabDrawables = getTabDrawables(); if (drawTabs) { - WebTabData lastTab = tabs.isEmpty() ? null : tabs.get(0); TabDrawable drawable = findTabDrawable(lastTab); if (drawable != null) { getTabBounds(rect, drawable.getPosition()); - final boolean closeHit = drawable.closeRipple.getBounds().contains((int) (event.getX() - rect.left), (int) (event.getY() - rect.centerY())); - if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) { - closeRippleHit = closeHit; - drawable.closeRipple.setState(closeHit ? new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled} : new int[] {}); - } else if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) { - if (closeRippleHit && event.getAction() == MotionEvent.ACTION_UP) { + if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) { + closeRippleHit = drawable.closeRipple.getBounds().contains((int) (x - rect.left), (int) (y - rect.centerY())); + hit = !closeRippleHit && rect.contains(x, y); + drawable.closeRipple.setState(closeRippleHit ? new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled} : new int[] {}); + } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { + if (hit && action == MotionEvent.ACTION_UP) { + click(); + } else if (closeRippleHit && action == MotionEvent.ACTION_UP) { removeTab(lastTab, success -> {}); } closeRippleHit = false; + hit = false; drawable.closeRipple.setState(new int[] {}); } for (int i = 0; i < tabDrawables.size(); ++i) { @@ -547,11 +543,32 @@ public boolean onTouchEvent(MotionEvent event) { } } } else { + hit = false; closeRippleHit = false; } + } else { + hit = false; + closeRippleHit = false; + } + return hit || closeRippleHit; + } + + public void click() { + final ArrayList tabs = getTabs(); + + final int count = tabs.size(); + if (count == 0) return; + WebTabData lastTab = tabs.get(tabs.size() - 1); + BottomSheetTabsOverlay overlay = LaunchActivity.instance == null ? null : LaunchActivity.instance.getBottomSheetTabsOverlay(); + if (overlay != null) { + overlay.stopAnimations(); + } + + if (count == 1 || overlay == null) { + openTab(lastTab); + } else { + overlay.openTabsView(); } - if (closeRippleHit) return true; - return super.onTouchEvent(event); } private final RectF rect = new RectF(); @@ -561,7 +578,7 @@ protected void dispatchDraw(Canvas canvas) { final ArrayList tabs = getTabs(); final ArrayList tabDrawables = getTabDrawables(); - if (actionBarLayout != null && actionBarLayout.bottomTabsProgress <= 0) { + if (bottomTabsProgress <= 0) { return; } @@ -839,6 +856,7 @@ public static class WebTabData { public Object previewNode; public boolean overrideActionBarColor; + public boolean overrideBackgroundColor; public int actionBarColorKey; public int actionBarColor; public int backgroundColor; @@ -853,6 +871,7 @@ public static class WebTabData { public String lastUrl; public boolean confirmDismiss; + public boolean fullscreen; public boolean fullsize; public boolean needsContext; @@ -871,6 +890,10 @@ public static class WebTabData { public float articleProgress; public ArticleViewer articleViewer; + public BotSensors sensors; + + public boolean orientationLocked; + public long getBotId() { if (props == null) return 0; return props.botId; @@ -907,4 +930,100 @@ public String getTitle() { } + private ValueAnimator bottomTabsAnimator; + public float bottomTabsProgress; + public int bottomTabsHeight; + + public void updateVisibility(boolean animated) { + if (bottomTabsHeight == getExpandedHeight()) + return; + if (bottomTabsAnimator != null) { + ValueAnimator prev = bottomTabsAnimator; + bottomTabsAnimator = null; + prev.cancel(); + } + bottomTabsHeight = getExpandedHeight(); + for (Runnable l : relayoutListeners) + l.run(); + if (animated) { + bottomTabsAnimator = ValueAnimator.ofFloat(bottomTabsProgress, bottomTabsHeight); + bottomTabsAnimator.addUpdateListener(anm -> { + bottomTabsProgress = (float) anm.getAnimatedValue(); + for (Runnable l : invalidateListeners) + l.run(); + invalidate(); + }); + bottomTabsAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (bottomTabsAnimator == animation) { + bottomTabsProgress = bottomTabsHeight; + for (Runnable l : invalidateListeners) + l.run(); + } + } + }); + bottomTabsAnimator.setDuration(AdjustPanLayoutHelper.keyboardDuration); + bottomTabsAnimator.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); + bottomTabsAnimator.start(); + } else { + bottomTabsProgress = bottomTabsHeight; + invalidate(); + } + } + + public static class ClipTools { + + private final BottomSheetTabs tabs; + private final RectF clipRect = new RectF(); + private final float[] clipRadius = new float[8]; + private final Path clipPath = new Path(); + private final Paint clipShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + public ClipTools(BottomSheetTabs tabs) { + this.tabs = tabs; + } + + public void clip(Canvas canvas, boolean withShadow, boolean isKeyboardVisible, int width, int height, float visible) { + final int bottomSheetHeight = (int) ((isKeyboardVisible ? 0 : tabs.getHeight(true)) * visible); + final int bottomRadius = Math.min(1, bottomSheetHeight / dp(60)) * dp(10); + if (bottomSheetHeight <= 0) + return; + + clipRadius[0] = clipRadius[1] = clipRadius[2] = clipRadius[3] = 0; // top + clipRadius[4] = clipRadius[5] = clipRadius[6] = clipRadius[7] = bottomRadius; // bottom + + clipPath.rewind(); + clipRect.set(0, 0, width, tabs.getY() + tabs.getHeight() - bottomSheetHeight); + clipPath.addRoundRect(clipRect, clipRadius, Path.Direction.CW); + + clipShadowPaint.setAlpha(0); + if (withShadow) { + clipShadowPaint.setShadowLayer(dp(2), 0, dp(1), 0x10000000); + canvas.drawPath(clipPath, clipShadowPaint); + } + canvas.clipPath(clipPath); + } + + } + + public int getHeight(boolean animated) { + if (animated) { + return (int) bottomTabsProgress; + } else { + return bottomTabsHeight; + } + } + + private final HashSet invalidateListeners = new HashSet<>(); + private final HashSet relayoutListeners = new HashSet<>(); + public void listen(Runnable invalidate, Runnable relayout) { + invalidateListeners.add(invalidate); + relayoutListeners.add(relayout); + } + public void stopListening(Runnable invalidate, Runnable relayout) { + invalidateListeners.remove(invalidate); + relayoutListeners.remove(relayout); + } + } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabsOverlay.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabsOverlay.java index 370b8b4ef66..d22800a0ea2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabsOverlay.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabsOverlay.java @@ -68,6 +68,7 @@ public interface Sheet { public void release(); public boolean isFullSize(); + public default boolean hadDialog() { return false; }; public boolean setDialog(BottomSheetTabDialog dialog); default void setLastVisible(boolean lastVisible) {}; @@ -503,8 +504,7 @@ public boolean dismissSheet(Sheet sheet) { dismissingSheet = sheet; sheet.setLastVisible(false); - sheet.getWindowView().setDrawingFromOverlay(true); - invalidate(); +// sheet.getWindowView().setDrawingFromOverlay(true); if (animator != null) { animator.cancel(); @@ -512,6 +512,12 @@ public boolean dismissSheet(Sheet sheet) { BottomSheetTabs.WebTabData tab = sheet.saveState(); dismissingTab = tabsView.pushTab(tab); + post(() -> { + if (sheet != null && sheet.getWindowView() != null) { + sheet.getWindowView().setDrawingFromOverlay(true); + } + }); + invalidate(); dismissProgress = 0; animator = ValueAnimator.ofFloat(0, 1); @@ -546,11 +552,8 @@ public void onAnimationEnd(Animator animation) { invalidate(); } }); - if (slowerDismiss || sheet.isFullSize()) { - AndroidUtilities.applySpring(animator, 220, 30, 1); - } else { - AndroidUtilities.applySpring(animator, 350, 30, 1); - } + AndroidUtilities.applySpring(animator, 220, 30, 1); + animator.setDuration((long) (animator.getDuration() * 1.1f)); animator.start(); slowerDismiss = false; @@ -804,9 +807,8 @@ private void drawTabsPreview(Canvas canvas) { float alpha = 1f; float top, bottom, y; top = paddingTop + dp(6) * Math.min(5, position); - bottom = thisHeight - paddingBottom - height * .26f;// - dp(6) * Math.min(5, count - position); + bottom = thisHeight - paddingBottom - height * .26f; y = top + (bottom - top) * scroll; - alpha = 1f; // Utilities.clamp(oscrollT * 4f + 1f, 1f, 0f); if (alpha <= 0) continue; 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 2c347452762..7c8fb3f464e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/EmojiThemes.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/EmojiThemes.java @@ -315,23 +315,28 @@ public SparseIntArray getPreviewColors(int currentAccount, int index) { } SparseIntArray fallbackKeys = Theme.getFallbackKeys(); - items.get(index).currentPreviewColors = new SparseIntArray(); - for (int i = 0; i < previewColorKeys.length; i++) { - int key = previewColorKeys[i]; - int colorIndex = currentColors.indexOfKey(key); - if (colorIndex >= 0) { - items.get(index).currentPreviewColors.put(key, currentColors.valueAt(colorIndex)); - } else { - int fallbackKey = fallbackKeys.get(key, -1); - if (fallbackKey >= 0) { - int fallbackIndex = currentColors.indexOfKey(fallbackKey); - if (fallbackIndex >= 0) { - items.get(index).currentPreviewColors.put(key, currentColors.valueAt(fallbackIndex)); + SparseIntArray array = new SparseIntArray(); + items.get(index).currentPreviewColors = array; + try { + for (int i = 0; i < previewColorKeys.length; i++) { + int key = previewColorKeys[i]; + int colorIndex = currentColors.indexOfKey(key); + if (colorIndex >= 0) { + array.put(key, currentColors.valueAt(colorIndex)); + } else { + int fallbackKey = fallbackKeys.get(key, -1); + if (fallbackKey >= 0) { + int fallbackIndex = currentColors.indexOfKey(fallbackKey); + if (fallbackIndex >= 0) { + array.put(key, currentColors.valueAt(fallbackIndex)); + } } } } + } catch (Exception e) { + FileLog.e(e); } - return items.get(index).currentPreviewColors; + return array; } public SparseIntArray createColors(int currentAccount, int index) { @@ -591,9 +596,13 @@ public void loadPreviewColors(int currentAccount) { private int getOrDefault(SparseIntArray colorsMap, int key) { if (colorsMap == null) return Theme.getDefaultColor(key); - int index = colorsMap.indexOfKey(key); - if (index >= 0) { - return colorsMap.valueAt(index); + try { + int index = colorsMap.indexOfKey(key); + if (index >= 0) { + return colorsMap.valueAt(index); + } + } catch (Exception e) { + FileLog.e(e); } return Theme.getDefaultColor(key); } 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 54506feba38..bc51e8bc50c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java @@ -4072,8 +4072,10 @@ public void run() { public static final int key_sheet_scrollUp = colorsCount++; public static final int key_sheet_other = colorsCount++; + public static final int key_bot_loadingIcon = colorsCount++; + public static final int key_gift_ribbon = colorsCount++; + public static final int key_gift_ribbon_soldout = colorsCount++; - //ununsed public static final int key_player_actionBarSelector = colorsCount++; public static final int key_player_actionBarTitle = colorsCount++; public static final int key_player_actionBarSubtitle = colorsCount++; @@ -4262,6 +4264,8 @@ public void run() { fallbackKeys.put(key_iv_background, key_windowBackgroundWhite); fallbackKeys.put(key_iv_backgroundGray, key_windowBackgroundGray); fallbackKeys.put(key_iv_navigationBackground, key_windowBackgroundGray); + fallbackKeys.put(key_bot_loadingIcon, key_groupcreate_spanBackground); + fallbackKeys.put(key_gift_ribbon_soldout, key_text_RedBold); fallbackKeys.put(key_iv_ab_progress, key_featuredStickers_addButton); fallbackKeys.put(key_dialogGiftsBackground, key_windowBackgroundGray); fallbackKeys.put(key_dialogGiftsTabText, key_windowBackgroundWhiteGrayText2); 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 e05f7489a2a..63ea6c0b416 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeColors.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeColors.java @@ -66,6 +66,9 @@ public static int[] createDefaultColors() { defaultColors[key_dialogCardShadow] = 0x30999999; defaultColors[key_dialogGiftsBackground] = 0xffF5F6F7; defaultColors[key_dialogGiftsTabText] = 0xFF56595C; + defaultColors[key_bot_loadingIcon] = 0xFFF2F2F2; + defaultColors[key_gift_ribbon] = 0xFF46A4F2; + defaultColors[key_gift_ribbon_soldout] = 0xffcc4747; defaultColors[key_windowBackgroundWhite] = 0xffffffff; defaultColors[key_windowBackgroundUnchecked] = 0xff9da7b1; @@ -843,6 +846,9 @@ public static SparseArray createColorKeysMap() { colorKeysMap.put(key_dialog_inlineProgress, "dialog_inlineProgress"); colorKeysMap.put(key_dialogSearchBackground, "dialogSearchBackground"); colorKeysMap.put(key_dialogSearchHint, "dialogSearchHint"); + colorKeysMap.put(key_bot_loadingIcon, "bot_loadingIcon"); + colorKeysMap.put(key_gift_ribbon, "gift_ribbon"); + colorKeysMap.put(key_gift_ribbon_soldout, "gift_ribbon_soldout"); colorKeysMap.put(key_dialogSearchIcon, "dialogSearchIcon"); colorKeysMap.put(key_dialogSearchText, "dialogSearchText"); colorKeysMap.put(key_dialogFloatingButton, "dialogFloatingButton"); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java index 06a4988de19..40cca6a120e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java @@ -8,6 +8,8 @@ package org.telegram.ui.Adapters; +import static org.telegram.messenger.AndroidUtilities.dp; + import android.content.Context; import android.os.SystemClock; import android.text.SpannableStringBuilder; @@ -72,10 +74,17 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { public final int VIEW_TYPE_CATEGORY_LIST = 6; public final int VIEW_TYPE_ADD_BY_PHONE = 7; public final int VIEW_TYPE_INVITE_CONTACT_CELL = 8; + public final int VIEW_TYPE_PUBLIC_POST = 9; private Context mContext; private Runnable searchRunnable; private Runnable searchRunnable2; + private int searchHashtagRequest = -1; + private Runnable searchHashtagRunnable; private ArrayList searchResult = new ArrayList<>(); + public int publicPostsTotalCount; + public int publicPostsLastRate; + public ArrayList publicPosts = new ArrayList<>(); + public String publicPostsHashtag; private final ArrayList searchContacts = new ArrayList<>(); private final ArrayList searchTopics = new ArrayList<>(); private ArrayList searchResultNames = new ArrayList<>(); @@ -186,7 +195,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType if (showPremiumBlock) { cell.showPremiumBlocked(); } - cell.setLayoutParams(new RecyclerView.LayoutParams(AndroidUtilities.dp(80), AndroidUtilities.dp(86))); + cell.setLayoutParams(new RecyclerView.LayoutParams(dp(80), dp(86))); return new RecyclerListView.Holder(cell); } @@ -757,6 +766,7 @@ public void putRecentSearch(final long did, TLObject object) { recentSearchObject.did = did; recentSearchObject.object = object; recentSearchObject.date = (int) (System.currentTimeMillis() / 1000); + filterRecent(lastSearchText != null ? lastSearchText.trim() : null); notifyDataSetChanged(); MessagesStorage.getInstance(currentAccount).getStorageQueue().postRunnable(() -> { try { @@ -800,6 +810,7 @@ public void clearRecentSearch() { queryFilter = new StringBuilder("1"); } final StringBuilder finalQueryFilter = queryFilter; + filterRecent(lastSearchText != null ? lastSearchText.trim() : null); notifyDataSetChanged(); MessagesStorage.getInstance(currentAccount).getStorageQueue().postRunnable(() -> { try { @@ -1000,7 +1011,7 @@ public void clearRecentHashtags() { int waitingResponseCount; - public void searchDialogs(String text, int folderId) { + public void searchDialogs(String text, int folderId, boolean allowPublicPosts) { if (text != null && text.equals(lastSearchText) && (folderId == this.folderId || TextUtils.isEmpty(text))) { return; } @@ -1014,6 +1025,14 @@ public void searchDialogs(String text, int folderId) { AndroidUtilities.cancelRunOnUIThread(searchRunnable2); searchRunnable2 = null; } + if (searchHashtagRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(searchHashtagRunnable); + searchHashtagRunnable = null; + } + if (searchHashtagRequest >= 0) { + ConnectionsManager.getInstance(currentAccount).cancelRequest(searchHashtagRequest, true); + searchHashtagRequest = -1; + } String query; if (text != null) { query = text.trim(); @@ -1027,6 +1046,10 @@ public void searchDialogs(String text, int folderId) { searchResult.clear(); searchResultNames.clear(); searchResultHashtags.clear(); + publicPostsTotalCount = 0; + publicPostsLastRate = 0; + publicPostsHashtag = null; + publicPosts.clear(); searchAdapterHelper.mergeResults(null, null); if (dialogsType != DialogsActivity.DIALOGS_TYPE_BOT_REQUEST_PEER) { searchAdapterHelper.queryServerSearch( @@ -1064,6 +1087,10 @@ public void searchDialogs(String text, int folderId) { } } else { searchAdapterHelper.mergeResults(searchResult, filtered2RecentSearchObjects); + publicPostsTotalCount = 0; + publicPostsLastRate = 0; + publicPostsHashtag = null; + publicPosts.clear(); if (needMessagesSearch != 2 && (query.startsWith("#") && query.length() == 1)) { messagesSearchEndReached = true; if (searchAdapterHelper.loadRecentHashtags()) { @@ -1095,6 +1122,27 @@ public void searchDialogs(String text, int folderId) { delegate.searchStateChanged(true, false); } + String hashtag = null, hashtagUsername = null; + if (allowPublicPosts && query != null) { + String tquery = query.trim(); + if (tquery.length() > 1 && (tquery.charAt(0) == '#' || tquery.charAt(0) == '$')) { + int atIndex = tquery.indexOf('@'); + hashtag = tquery.substring(1); + if (atIndex >= 0) { + hashtagUsername = tquery.substring(atIndex + 1); + } + } + } + +// if (hashtagUsername != null) { +// TLObject chat = MessagesController.getInstance(currentAccount).getUserOrChat(hashtagUsername); +// if (chat == null) { +// +// } +// TLRPC.TL_messages_search +// return; +// } + Utilities.searchQueue.postRunnable(searchRunnable = () -> { searchRunnable = null; searchDialogsInternal(query, searchId); @@ -1133,6 +1181,52 @@ public void searchDialogs(String text, int folderId) { } }); }, 300); + + final String finalHashtag = hashtag; + + if (finalHashtag != null) { + waitingResponseCount++; + AndroidUtilities.runOnUIThread(searchHashtagRunnable = () -> { + searchHashtagRunnable = null; + if (searchId != lastSearchId) { + return; + } + if (searchHashtagRequest >= 0) { + ConnectionsManager.getInstance(currentAccount).cancelRequest(searchHashtagRequest, true); + } + TLRPC.TL_channels_searchPosts req = new TLRPC.TL_channels_searchPosts(); + req.hashtag = finalHashtag; + req.limit = 3; + req.offset_peer = new TLRPC.TL_inputPeerEmpty(); + searchHashtagRequest = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (searchId != lastSearchId) { + return; + } + if (res instanceof TLRPC.messages_Messages) { + TLRPC.messages_Messages msgs = (TLRPC.messages_Messages) res; + int totalCount = 0; + if (msgs instanceof TLRPC.TL_messages_messages) { + totalCount = ((TLRPC.TL_messages_messages) msgs).messages.size(); + } else if (msgs instanceof TLRPC.TL_messages_messagesSlice) { + totalCount = ((TLRPC.TL_messages_messagesSlice) msgs).count; + } + publicPostsTotalCount = totalCount; + publicPostsLastRate = msgs.next_rate; + publicPostsHashtag = finalHashtag; + MessagesController.getInstance(currentAccount).putUsers(msgs.users, false); + MessagesController.getInstance(currentAccount).putChats(msgs.chats, false); + for (int i = 0; i < msgs.messages.size(); ++i) { + TLRPC.Message msg = msgs.messages.get(i); + publicPosts.add(new MessageObject(currentAccount, msg, false, true)); + } + if (delegate != null) { + delegate.searchStateChanged(waitingResponseCount > 0, true); + } + notifyDataSetChanged(); + } + })); + }, 300); + } } } @@ -1152,6 +1246,9 @@ public int getItemCount() { return 0; } int count = 0; + if (!publicPosts.isEmpty()) { + count += publicPosts.size() + 1; + } if (!searchResultHashtags.isEmpty()) { count += searchResultHashtags.size() + 1; return count; @@ -1183,7 +1280,7 @@ public int getItemCount() { if (phoneCount > 3 && phoneCollapsed) { phoneCount = 3; } - if (resultsCount + localServerCount > 0 && (getRecentItemsCount() > 0 || !searchTopics.isEmpty())) { + if (resultsCount + localServerCount > 0 && (getRecentItemsCount() > 0 || !searchTopics.isEmpty() || !publicPosts.isEmpty())) { count++; } if (globalCount != 0) { @@ -1213,6 +1310,12 @@ public int getItemCount() { } public Object getItem(int i) { + if (!publicPosts.isEmpty()) { + if (i > 0 && i - 1 < publicPosts.size()) { + return publicPosts.get(i - 1); + } + i -= (publicPosts.size() + 1); + } if (!searchResultHashtags.isEmpty()) { if (i > 0) { return searchResultHashtags.get(i - 1); @@ -1258,7 +1361,7 @@ public Object getItem(int i) { ArrayList phoneSearch = searchAdapterHelper.getPhoneSearch(); int localCount = searchResult.size(); int localServerCount = localServerSearch.size(); - if (localCount + localServerCount > 0 && (getRecentItemsCount() > 0 || !searchTopics.isEmpty())) { + if (localCount + localServerCount > 0 && (getRecentItemsCount() > 0 || !searchTopics.isEmpty() || !publicPosts.isEmpty())) { if (i == 0) { return null; } @@ -1308,6 +1411,9 @@ public boolean isGlobalSearch(int i) { if (!searchResultHashtags.isEmpty()) { return false; } + if (!publicPosts.isEmpty()) { + i -= 1 + publicPosts.size(); + } if (isRecentSearchDisplayed()) { int offset = (hasHints() ? 1 : 0); ArrayList recent = searchWas ? filtered2RecentSearchObjects : filteredRecentSearchObjects; @@ -1336,7 +1442,7 @@ public boolean isGlobalSearch(int i) { } i -= contactsCount + 1; } - if (localCount + localServerCount > 0 && (getRecentItemsCount() > 0 || !searchTopics.isEmpty())) { + if (localCount + localServerCount > 0 && (getRecentItemsCount() > 0 || !searchTopics.isEmpty() || !publicPosts.isEmpty())) { if (i == 0) { return false; } @@ -1392,6 +1498,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType view = new GraySectionCell(mContext); break; case VIEW_TYPE_DIALOG_CELL: + case VIEW_TYPE_PUBLIC_POST: view = new DialogCell(null, mContext, false, true) { @Override public boolean isForumCell() { @@ -1464,7 +1571,7 @@ public boolean supportsPredictiveItemAnimations() { break; } if (viewType == 5) { - view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, AndroidUtilities.dp(86))); + view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, dp(86))); } else { view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT)); } @@ -1495,28 +1602,21 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (obj instanceof TLRPC.User) { user = (TLRPC.User) obj; - un = UserObject.getPublicUsername(user); - if (un != null && lastSearchText != null && !un.toLowerCase().contains(lastSearchText.toLowerCase())) { - if (user.usernames != null) { - for (int i = 0; i < user.usernames.size(); ++i) { - TLRPC.TL_username u = user.usernames.get(i); - if (u != null && u.active && u.username.toLowerCase().contains(lastSearchText.toLowerCase())) { - un = u.username; - } - } - } - } + un = DialogObject.getPublicUsername(user, currentMessagesQuery); } else if (obj instanceof TLRPC.Chat) { chat = MessagesController.getInstance(currentAccount).getChat(((TLRPC.Chat) obj).id); if (chat == null) { chat = (TLRPC.Chat) obj; } - un = ChatObject.getPublicUsername(chat); + un = DialogObject.getPublicUsername(chat, currentMessagesQuery); } else if (obj instanceof TLRPC.EncryptedChat) { encryptedChat = MessagesController.getInstance(currentAccount).getEncryptedChat(((TLRPC.EncryptedChat) obj).id); user = MessagesController.getInstance(currentAccount).getUser(encryptedChat.user_id); } + if (!publicPosts.isEmpty()) { + position -= publicPosts.size() + 1; + } if (isRecentSearchDisplayed()) { if (position < getRecentItemsCount()) { cell.useSeparator = position != getRecentItemsCount() - 1; @@ -1531,7 +1631,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ArrayList phoneSearch = searchAdapterHelper.getPhoneSearch(); int localCount = searchResult.size(); int localServerCount = searchAdapterHelper.getLocalServerSearch().size(); - if (localCount + localServerCount > 0 && (getRecentItemsCount() > 0 || !searchTopics.isEmpty())) { + if (localCount + localServerCount > 0 && (getRecentItemsCount() > 0 || !searchTopics.isEmpty() || !publicPosts.isEmpty())) { position--; } int phoneCount = phoneSearch.size(); @@ -1621,7 +1721,17 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else { username = membersString; } + } else if (user != null && user.bot && user.bot_active_users != 0) { + String membersString = LocaleController.formatPluralStringSpaced("BotUsersShort", user.bot_active_users); + if (username instanceof SpannableStringBuilder) { + ((SpannableStringBuilder) username).append(", ").append(membersString); + } else if (!TextUtils.isEmpty(username)) { + username = TextUtils.concat(username, ", ", membersString); + } else { + username = membersString; + } } + cell.allowBotOpenButton(isRecent, this::openBotApp); cell.setData(user != null ? user : chat, encryptedChat, name, username, true, savedMessages); cell.setChecked(delegate.isSelected(cell.getDialogId()), oldDialogId == cell.getDialogId()); break; @@ -1636,7 +1746,16 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { }); } else { int rawPosition = position; - if (isRecentSearchDisplayed() || !searchTopics.isEmpty() || !searchContacts.isEmpty()) { + if (!publicPosts.isEmpty()) { + if (position == 0) { + cell.setText(LocaleController.getString(R.string.PublicPostsTabs), AndroidUtilities.replaceArrows(LocaleController.getString(R.string.PublicPostsMore), false, dp(-2), dp(1)), v -> { + openPublicPosts(); + }); + return; + } + position -= 1 + publicPosts.size(); + } + if (isRecentSearchDisplayed() || !searchTopics.isEmpty() || !searchContacts.isEmpty() || !publicPosts.isEmpty()) { int offset = hasHints() ? 1 : 0; if (position < offset) { cell.setText(LocaleController.getString(R.string.ChatHints)); @@ -1785,7 +1904,8 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } break; } - case VIEW_TYPE_DIALOG_CELL: { + case VIEW_TYPE_DIALOG_CELL: + case VIEW_TYPE_PUBLIC_POST: { DialogCell cell = (DialogCell) holder.itemView; cell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); cell.useSeparator = (position != getItemCount() - 1); @@ -1840,6 +1960,12 @@ public int getItemViewType(int i) { if (!searchResultHashtags.isEmpty()) { return i == 0 ? VIEW_TYPE_GRAY_SECTION : VIEW_TYPE_HASHTAG_CELL; } + if (!publicPosts.isEmpty()) { + if (i == 0) return VIEW_TYPE_GRAY_SECTION; + i--; + if (i < publicPosts.size()) return VIEW_TYPE_PUBLIC_POST; + i -= publicPosts.size(); + } if (isRecentSearchDisplayed()) { int offset = hasHints() ? 1 : 0; if (i < offset) { @@ -1873,7 +1999,7 @@ public int getItemViewType(int i) { ArrayList globalSearch = searchAdapterHelper.getGlobalSearch(); int localCount = searchResult.size(); int localServerCount = searchAdapterHelper.getLocalServerSearch().size(); - if (localCount + localServerCount > 0 && (getRecentItemsCount() > 0 || !searchTopics.isEmpty())) { + if (localCount + localServerCount > 0 && (getRecentItemsCount() > 0 || !searchTopics.isEmpty() || !publicPosts.isEmpty())) { if (i == 0) { return VIEW_TYPE_GRAY_SECTION; } @@ -2023,4 +2149,12 @@ private static class ContactEntry { String q2; ContactsController.Contact contact; } + + protected void openPublicPosts() { + + } + + protected void openBotApp(TLRPC.User bot) { + + } } 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 43bc02b95d4..a819c37ba6a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java @@ -8,17 +8,23 @@ package org.telegram.ui.Adapters; +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.ui.PremiumPreviewFragment.applyNewSpan; + import android.Manifest; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.graphics.drawable.ColorDrawable; import android.location.Location; import android.os.Build; import android.text.Spanned; import android.text.TextUtils; import android.util.TypedValue; +import android.view.Gravity; import android.view.View; import android.view.ViewGroup; +import android.widget.LinearLayout; import android.widget.TextView; import androidx.collection.LongSparseArray; @@ -55,9 +61,14 @@ import org.telegram.ui.Cells.ContextLinkCell; import org.telegram.ui.Cells.MentionCell; import org.telegram.ui.Cells.StickerCell; +import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.ChatActivity; import org.telegram.ui.Components.AnimatedEmojiSpan; +import org.telegram.ui.Components.AvatarDrawable; +import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.EmojiView; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RecyclerListView; import java.io.File; @@ -84,14 +95,18 @@ public interface MentionsAdapterDelegate { private final boolean USE_DIVIDERS = false; private int currentAccount = UserConfig.selectedAccount; - private Context mContext; + private final Context mContext; private long dialog_id; private long threadMessageId; + private final boolean stories; private TLRPC.ChatFull info; private SearchAdapterHelper searchAdapterHelper; private ArrayList searchResultUsernames; private LongSparseArray searchResultUsernamesMap; private Runnable searchGlobalRunnable; + private String hintHashtag; + private boolean hintHashtagDivider; + private HashtagHint topHint, bottomHint; private ArrayList searchResultHashtags; private ArrayList searchResultCommands; private ArrayList searchResultCommandsHelp; @@ -122,7 +137,7 @@ public interface MentionsAdapterDelegate { private int channelReqId; private boolean isSearchingMentions; private TLRPC.User user; - private TLRPC.Chat chat; + public TLRPC.Chat chat; private boolean searchInDailogs = false; @@ -153,7 +168,7 @@ public interface MentionsAdapterDelegate { private boolean delayLocalResults; private Runnable checkAgainRunnable; - private ChatActivity parentFragment; + public ChatActivity parentFragment; private final Theme.ResourcesProvider resourcesProvider; private static class StickerResult { @@ -187,12 +202,13 @@ public void stop() { } }; - public MentionsAdapter(Context context, boolean darkTheme, long did, long threadMessageId, MentionsAdapterDelegate mentionsAdapterDelegate, Theme.ResourcesProvider resourcesProvider) { + public MentionsAdapter(Context context, boolean darkTheme, long did, long threadMessageId, MentionsAdapterDelegate mentionsAdapterDelegate, Theme.ResourcesProvider resourcesProvider, boolean stories) { this.resourcesProvider = resourcesProvider; mContext = context; delegate = mentionsAdapterDelegate; isDarkTheme = darkTheme; dialog_id = did; + this.stories = stories; this.threadMessageId = threadMessageId; searchAdapterHelper = new SearchAdapterHelper(true); searchAdapterHelper.setDelegate(new SearchAdapterHelper.SearchAdapterHelperDelegate() { @@ -1068,6 +1084,10 @@ public int compare(StickerResult lhs, StickerResult rhs) { } final MessagesController messagesController = MessagesController.getInstance(currentAccount); int dogPostion = -1; + final String oldHintHashtag = hintHashtag; + final boolean oldHintHashtagDivider = hintHashtagDivider; + hintHashtag = null; + hintHashtagDivider = false; if (usernameOnly) { result.append(text.substring(1)); resultStartPosition = 0; @@ -1096,6 +1116,11 @@ public int compare(StickerResult lhs, StickerResult rhs) { break; } } else if (ch == '#') { + if (ChatObject.isChannelAndNotMegaGroup(currentChat) && !TextUtils.isEmpty(ChatObject.getPublicUsername(currentChat))) { + hintHashtag = text.substring(a); + if (hintHashtag.length() < 4 || !hintHashtag.matches("^[#$][\\p{L}_-]+$")) + hintHashtag = null; + } if (searchAdapterHelper.loadRecentHashtags()) { foundType = 1; resultStartPosition = a; @@ -1127,6 +1152,14 @@ public int compare(StickerResult lhs, StickerResult rhs) { result.insert(0, ch); } } + if (oldHintHashtag == null && hintHashtag != null) { + notifyItemRangeInserted(0, 2); + } else if (oldHintHashtag != null && hintHashtag == null) { + notifyItemRangeRemoved(0, 2); + } else { + if (topHint != null) topHint.set(0, hintHashtag, currentChat); + if (bottomHint != null) bottomHint.set(1, hintHashtag, currentChat); + } if (foundType == -1) { contextMedia = false; searchResultBotContext = null; @@ -1428,12 +1461,12 @@ public void run() { contextMedia = false; searchResultBotContext = null; notifyDataSetChanged(); - delegate.needChangePanelVisibility(!searchResultHashtags.isEmpty()); + delegate.needChangePanelVisibility(!searchResultHashtags.isEmpty() || hintHashtag != null); } else if (foundType == 2) { ArrayList newResult = new ArrayList<>(); ArrayList newResultHelp = new ArrayList<>(); ArrayList newResultUsers = new ArrayList<>(); - String command = result.toString().toLowerCase(); + final String command = result.toString().toLowerCase(); for (int b = 0; b < botInfo.size(); b++) { TL_bots.BotInfo info = botInfo.valueAt(b); for (int a = 0; a < info.commands.size(); a++) { @@ -1446,13 +1479,15 @@ public void run() { } } if (parentFragment != null && !DialogObject.isEncryptedDialog(dialog_id) && parentFragment.getChatMode() == 0 && parentFragment.getCurrentUser() != null && !parentFragment.getCurrentUser().bot && !UserObject.isReplyUser(parentFragment.getCurrentUser()) && !UserObject.isService(parentFragment.getCurrentUser().id)) { - QuickRepliesController quickRepliesController = QuickRepliesController.getInstance(currentAccount); + final QuickRepliesController quickRepliesController = QuickRepliesController.getInstance(currentAccount); quickRepliesController.load(); quickRepliesQuery = command; - quickReplies = new ArrayList(); + quickReplies = new ArrayList<>(); for (int i = 0; i < quickRepliesController.replies.size(); i++) { - QuickRepliesController.QuickReply reply = quickRepliesController.replies.get(i); - if (!reply.isSpecial() && reply.name.startsWith(command)) { + final QuickRepliesController.QuickReply reply = quickRepliesController.replies.get(i); + if (reply.isSpecial()) continue; + final String replyName = reply.name.toLowerCase(); + if (replyName.startsWith(command) || AndroidUtilities.translitSafe(replyName).startsWith(command)) { quickReplies.add(reply); } } @@ -1570,20 +1605,24 @@ public int getItemCountInternal() { if (foundContextBot != null && !inlineMediaEnabled) { return 1; } + int count = 0; + if (hintHashtag != null) { + count += 2;// + (!searchResultHashtags.isEmpty() ? 1 : 0); + } if (stickers != null) { - return stickers.size(); + count += stickers.size(); } else if (searchResultBotContext != null) { - return searchResultBotContext.size() + (searchResultBotContextSwitch != null || searchResultBotWebViewSwitch != null ? 1 : 0); + count += searchResultBotContext.size() + (searchResultBotContextSwitch != null || searchResultBotWebViewSwitch != null ? 1 : 0); } else if (searchResultUsernames != null) { - return searchResultUsernames.size(); + count += searchResultUsernames.size(); } else if (searchResultHashtags != null) { - return searchResultHashtags.size(); + count += searchResultHashtags.size(); } else if (searchResultCommands != null || quickReplies != null) { - return (quickReplies == null ? 0 : quickReplies.size()) + (searchResultCommands == null ? 0 : searchResultCommands.size()); + count += (quickReplies == null ? 0 : quickReplies.size()) + (searchResultCommands == null ? 0 : searchResultCommands.size()); } else if (searchResultSuggestions != null) { - return searchResultSuggestions.size(); + count += searchResultSuggestions.size(); } - return 0; + return count; } public void clear(boolean safe) { @@ -1591,6 +1630,7 @@ public void clear(boolean safe) { return; } foundContextBot = null; + hintHashtag = null; if (stickers != null) { stickers.clear(); } @@ -1619,6 +1659,12 @@ public void clear(boolean safe) { @Override public int getItemViewType(int position) { + if (hintHashtag != null) { + if (position < 2) return 6; + position -= 2; +// if (!searchResultHashtags.isEmpty() && position == 0) return 7; +// position--; + } if (stickers != null) { return 4; } else if (foundContextBot != null && !inlineMediaEnabled) { @@ -1640,6 +1686,12 @@ public void addHashtagsFromMessage(CharSequence message) { } public int getItemPosition(int i) { + if (hintHashtag != null) { + if (i < 2) return 0; + i -= 2; +// if (!searchResultHashtags.isEmpty() && i == 0) return 0; +// i--; + } if (searchResultBotContext != null && (searchResultBotContextSwitch != null || searchResultBotWebViewSwitch != null)) { i--; } @@ -1647,10 +1699,22 @@ public int getItemPosition(int i) { } public Object getItemParent(int i) { + if (hintHashtag != null) { + if (i < 2) return null; + i -= 2; +// if (!searchResultHashtags.isEmpty() && i == 0) return null; +// i--; + } return stickers != null && i >= 0 && i < stickers.size() ? stickers.get(i).parent : null; } public Object getItem(int i) { + if (hintHashtag != null) { + if (i < 2) return null; + i -= 2; +// if (!searchResultHashtags.isEmpty() && i == 0) return null; +// i--; + } if (stickers != null) { return i >= 0 && i < stickers.size() ? stickers.get(i).sticker : null; } else if (searchResultBotContext != null) { @@ -1757,7 +1821,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType break; case 3: TextView textView = new TextView(mContext); - textView.setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8)); + textView.setPadding(dp(8), dp(8), dp(8), dp(8)); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); textView.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteGrayText2)); view = textView; @@ -1765,6 +1829,20 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType case 5: view = new QuickRepliesActivity.QuickReplyView(mContext, false, resourcesProvider); break; + case 6: + view = new HashtagHint(mContext, stories, resourcesProvider); + break; + case 7: + view = new View(mContext) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(dp(8), MeasureSpec.EXACTLY)); + } + }; + CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(stories ? Theme.multAlpha(0xFFFFFFFF, .15f) : Theme.getColor(Theme.key_windowBackgroundGray, resourcesProvider)), Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.getColor(Theme.key_windowBackgroundGrayShadow, resourcesProvider)), 0, 0); + combinedDrawable.setFullsize(true); + view.setBackground(combinedDrawable); + break; case 4: default: view = new StickerCell(mContext, resourcesProvider); @@ -1775,6 +1853,10 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + if (hintHashtag != null) { + position -= 2; +// if (!searchResultHashtags.isEmpty()) position--; + } int type = holder.getItemViewType(); if (type == 4) { StickerCell stickerCell = (StickerCell) holder.itemView; @@ -1814,6 +1896,18 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ((ContextLinkCell) holder.itemView).setLink(searchResultBotContext.get(position), foundContextBot, contextMedia, position != searchResultBotContext.size() - 1, hasTop && position == 0, "gif".equals(searchingContextUsername)); } } + } else if (type == 6) { + HashtagHint hint = (HashtagHint) holder.itemView; + position += 2; + if (position == 0) topHint = hint; + else bottomHint = hint; + TLRPC.Chat chat = MentionsAdapter.this.chat; + if (chat == null && parentFragment != null) { + chat = parentFragment.getCurrentChat(); + } + hint.set(position, hintHashtag, chat); + } else if (type == 7) { + } else { MentionCell cell = (MentionCell) holder.itemView; if (searchResultUsernames != null) { @@ -1891,4 +1985,77 @@ public void setAllowBots(boolean allowBots) { public void setAllowChats(boolean allowChats) { this.allowChats = allowChats; } + + public String getHashtagHint() { + return hintHashtag; + } + + public boolean isLocalHashtagHint(int position) { + return hintHashtag != null && position == 1; + } + + public boolean isGlobalHashtagHint(int position) { + return hintHashtag != null && position == 0; + } + + public static class HashtagHint extends LinearLayout { + + private final Theme.ResourcesProvider resourcesProvider; + private final BackupImageView imageView; + private final AvatarDrawable avatarDrawable = new AvatarDrawable(); + private final LinearLayout textLayout; + private final TextView titleView; + private final TextView textView; + private final boolean transparent; + + public HashtagHint(Context context, boolean transparent, Theme.ResourcesProvider resourcesProvider) { + super(context); + this.resourcesProvider = resourcesProvider; + this.transparent = transparent; + + setOrientation(HORIZONTAL); + imageView = new BackupImageView(context); + imageView.setRoundRadius(dp(28)); + addView(imageView, LayoutHelper.createLinear(28, 28, Gravity.CENTER_VERTICAL | Gravity.LEFT, 12, 0, 12, 0)); + + textLayout = new LinearLayout(context); + textLayout.setOrientation(VERTICAL); + addView(textLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL | Gravity.TOP, 0, 4, 12, 4)); + + titleView = new TextView(context); + titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); + titleView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + textLayout.addView(titleView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + textView = new TextView(context); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); + textView.setTextColor(transparent ? Theme.multAlpha(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider), .5f) : Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider)); + textLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } + + public void set(int type, String hashtag, TLRPC.Chat chat) { + if (hashtag == null) return; + if (type == 0) { + CombinedDrawable drawable = new CombinedDrawable( + Theme.createRoundRectDrawable(dp(28), Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider)), + getContext().getResources().getDrawable(R.drawable.menu_hashtag).mutate() + ); + drawable.setIconOffset(dp(-0.66f), 0); + drawable.setIconSize(dp(20), dp(20)); + imageView.setImageDrawable(drawable); + titleView.setText(LocaleController.formatString(R.string.HashtagSuggestion1Title, hashtag)); + textView.setText(LocaleController.getString(R.string.HashtagSuggestion1Text)); + } else { + avatarDrawable.setInfo(chat); + imageView.setForUserOrChat(chat, avatarDrawable); + titleView.setText(applyNewSpan(LocaleController.formatString(R.string.HashtagSuggestion2Title, hashtag + "@" + ChatObject.getPublicUsername(chat)), 8)); + textView.setText(LocaleController.getString(R.string.HashtagSuggestion2Text)); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), heightMeasureSpec); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MessagesSearchAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MessagesSearchAdapter.java index 00b70eb06f1..a53cf75d490 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MessagesSearchAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MessagesSearchAdapter.java @@ -10,17 +10,24 @@ import static org.telegram.messenger.AndroidUtilities.dp; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; import android.text.TextUtils; import android.util.TypedValue; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import org.telegram.messenger.AndroidUtilities; @@ -34,22 +41,27 @@ import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Cells.DialogCell; import org.telegram.ui.Components.AvatarsDrawable; +import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.FlickerLoadingView; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.UItem; import org.telegram.ui.Stories.StoriesController; +import org.telegram.ui.Stories.StoriesListPlaceProvider; import java.util.ArrayList; import java.util.HashSet; public class MessagesSearchAdapter extends RecyclerListView.SelectionAdapter implements NotificationCenter.NotificationCenterDelegate { - private Context mContext; - private HashSet messageIds = new HashSet<>(); - private ArrayList searchResultMessages = new ArrayList<>(); + private final Context mContext; + private final HashSet messageIds = new HashSet<>(); + private final ArrayList searchResultMessages = new ArrayList<>(); + private final BaseFragment fragment; public boolean containsStories; @@ -62,13 +74,15 @@ public class MessagesSearchAdapter extends RecyclerListView.SelectionAdapter imp private boolean isSavedMessages; - public MessagesSearchAdapter(Context context, Theme.ResourcesProvider resourcesProvider, int searchType, boolean isSavedMessages) { + public MessagesSearchAdapter(Context context, BaseFragment fragment, Theme.ResourcesProvider resourcesProvider, int searchType, boolean isSavedMessages) { this.resourcesProvider = resourcesProvider; mContext = context; + this.fragment = fragment; this.searchType = searchType; this.isSavedMessages = isSavedMessages; } + public String storiesListQuery; public StoriesController.SearchStoriesList storiesList; public void setStoriesList(StoriesController.SearchStoriesList storiesList) { this.storiesList = storiesList; @@ -83,12 +97,20 @@ public void setStoriesList(StoriesController.SearchStoriesList storiesList) { } }; - public void searchStories(String hashtag, boolean instant) { -// if (hashtag.startsWith("$")) hashtag = hashtag.substring(1); -// if (hashtag.startsWith("#")) hashtag = hashtag.substring(1); + public void searchStories(String query, boolean instant) { + if (TextUtils.equals(storiesListQuery, query)) return; - final String currentHashtag = storiesList == null ? "" : storiesList.query; - if (TextUtils.equals(currentHashtag, hashtag)) return; + String hashtag = null, username = null; + String tquery = query.trim(); + if (tquery.charAt(0) == '$' || tquery.charAt(0) == '#') { + int atIndex = tquery.indexOf('@'); + if (atIndex >= 0) { + hashtag = tquery.substring(0, atIndex); + username = tquery.substring(atIndex + 1); + } else { + hashtag = tquery; + } + } final boolean wereContainingStories = containsStories; @@ -98,7 +120,8 @@ public void searchStories(String hashtag, boolean instant) { } if (!TextUtils.isEmpty(hashtag)) { - storiesList = new StoriesController.SearchStoriesList(currentAccount, hashtag); + storiesListQuery = query; + storiesList = new StoriesController.SearchStoriesList(currentAccount, username, hashtag); if (instant) { loadStories.run(); } else { @@ -125,7 +148,7 @@ public void didReceivedNotification(int id, int account, Object... args) { public void notifyDataSetChanged() { final int oldItemsCount = getItemCount(); - containsStories = storiesList != null && storiesList.getCount() > 0; + containsStories = false;//storiesList != null && storiesList.getCount() > 0; searchResultMessages.clear(); messageIds.clear(); @@ -153,7 +176,7 @@ public void notifyDataSetChanged() { final int newItemsCount = getItemCount(); if (oldItemsCount < newItemsCount) { - notifyItemRangeChanged(oldItemsCount - oldFlickerCount, oldFlickerCount); + if (oldFlickerCount > 0) notifyItemRangeChanged(oldItemsCount - oldFlickerCount, oldFlickerCount); notifyItemRangeInserted(oldItemsCount, newItemsCount - oldItemsCount); } else { super.notifyDataSetChanged(); @@ -230,6 +253,41 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { useMe = true; } cell.setDialog(did, messageObject, date, useMe, false); + cell.setDialogCellDelegate(new DialogCell.DialogCellDelegate() { + @Override + public void onButtonClicked(DialogCell dialogCell) { + + } + + @Override + public void onButtonLongPress(DialogCell dialogCell) { + + } + + @Override + public boolean canClickButtonInside() { + return false; + } + + @Override + public void openStory(DialogCell dialogCell, Runnable onDone) { + if (MessagesController.getInstance(currentAccount).getStoriesController().hasStories(dialogCell.getDialogId())) { + fragment.getOrCreateStoryViewer().doOnAnimationReady(onDone); + fragment.getOrCreateStoryViewer().open(mContext, dialogCell.getDialogId(), StoriesListPlaceProvider.of((RecyclerListView) dialogCell.getParent())); + return; + } + } + + @Override + public void showChatPreview(DialogCell dialogCell) { + + } + + @Override + public void openHiddenStories() { + + } + }); } else if (holder.getItemViewType() == 2) { ((StoriesView) holder.itemView).set(storiesList); } @@ -261,8 +319,9 @@ public static class StoriesView extends FrameLayout { private final Theme.ResourcesProvider resourcesProvider; private final AvatarsDrawable avatarsDrawable; - private final TextView titleTextView; - private final TextView subtitleTextView; + private final TextView[] titleTextView = new TextView[2]; + private final TextView[] subtitleTextView = new TextView[2]; + private final ImageView arrowView; public StoriesView(Context context, Theme.ResourcesProvider resourcesProvider) { super(context); @@ -276,16 +335,25 @@ public StoriesView(Context context, Theme.ResourcesProvider resourcesProvider) { avatarsDrawable.drawStoriesCircle = true; avatarsDrawable.setSize(dp(22)); - titleTextView = new TextView(context); - titleTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); - titleTextView.setTypeface(AndroidUtilities.bold()); - titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP, 76, 7, 12, 0)); + for (int i = 0; i < 2; ++i) { + titleTextView[i] = new TextView(context); + titleTextView[i].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + titleTextView[i].setTypeface(AndroidUtilities.bold()); + titleTextView[i].setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + titleTextView[i].setVisibility(i == 0 ? View.VISIBLE : View.GONE); + addView(titleTextView[i], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP, 76, 7, 40, 0)); + + subtitleTextView[i] = new TextView(context); + subtitleTextView[i].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider)); + subtitleTextView[i].setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); + subtitleTextView[i].setVisibility(i == 0 ? View.VISIBLE : View.GONE); + addView(subtitleTextView[i], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP, 76, 26.33f, 40, 0)); + } - subtitleTextView = new TextView(context); - subtitleTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider)); - subtitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); - addView(subtitleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP, 76, 26.33f, 12, 0)); + arrowView = new ImageView(context); + arrowView.setImageResource(R.drawable.msg_arrowright); + arrowView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogSearchHint, resourcesProvider), PorterDuff.Mode.SRC_IN)); + addView(arrowView, LayoutHelper.createFrame(24, 24, Gravity.CENTER_VERTICAL | Gravity.RIGHT, 0, 0, 8.66f, 0)); } @Override @@ -293,12 +361,15 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(dp(48), MeasureSpec.EXACTLY)); } - public void set(StoriesController.SearchStoriesList list) { + public boolean set(StoriesController.SearchStoriesList list) { int actualCount = 0; for (int i = 0; i < list.messageObjects.size() && actualCount < 3; ++i) { MessageObject msg = list.messageObjects.get(i); final long dialogId = msg.storyItem.dialogId; - if (dialogId >= 0) { + if (!TextUtils.isEmpty(list.username) || true) { + avatarsDrawable.setObject(actualCount, list.currentAccount, msg.storyItem); + actualCount++; + } else if (dialogId >= 0) { TLRPC.User user = MessagesController.getInstance(list.currentAccount).getUser(dialogId); if (user != null) { avatarsDrawable.setObject(actualCount, list.currentAccount, user); @@ -315,14 +386,75 @@ public void set(StoriesController.SearchStoriesList list) { avatarsDrawable.setCount(actualCount); avatarsDrawable.commitTransition(false); - titleTextView.setText(LocaleController.formatPluralStringSpaced("HashtagStoriesFound", list.getCount())); - subtitleTextView.setText(LocaleController.formatString(R.string.HashtagStoriesFoundSubtitle, list.query)); + if (!TextUtils.isEmpty(list.username)) { + titleTextView[0].setText(AndroidUtilities.replaceSingleLink(LocaleController.formatPluralStringSpaced("HashtagStoriesFoundChannel", list.getCount(), "@" + list.username), Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider), null)); + } else { + titleTextView[0].setText(LocaleController.formatPluralStringSpaced("HashtagStoriesFound", list.getCount())); + } + subtitleTextView[0].setText(LocaleController.formatString(R.string.HashtagStoriesFoundSubtitle, list.query)); + + return actualCount > 0; + } + + public void setMessages(int messagesCount, String hashtag, String username) { + if (!TextUtils.isEmpty(username)) { + titleTextView[1].setText(AndroidUtilities.replaceSingleLink(LocaleController.formatPluralStringSpaced("HashtagMessagesFoundChannel", messagesCount, "@" + username), Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider), null)); + } else { + titleTextView[1].setText(LocaleController.formatPluralStringSpaced("HashtagMessagesFound", messagesCount)); + } + subtitleTextView[1].setText(LocaleController.formatString(R.string.HashtagMessagesFoundSubtitle, hashtag)); + } + + private float transitValue; + private ValueAnimator transitionAnimator; + public void transition(boolean stories) { + if (transitionAnimator != null) { + transitionAnimator.cancel(); + } + transitionAnimator = ValueAnimator.ofFloat(transitValue, stories ? 1.0f : 0.0f); + transitionAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(@NonNull ValueAnimator animation) { + transitValue = (float) animation.getAnimatedValue(); + invalidate(); + for (int i = 0; i < 2; ++i) { + titleTextView[i].setTranslationX(AndroidUtilities.lerp(0, -dp(62), transitValue)); + titleTextView[i].setVisibility(View.VISIBLE); + titleTextView[i].setAlpha(AndroidUtilities.lerp(i == 0 ? 1.0f : 0.0f, i == 1 ? 1.0f : 0.0f, transitValue)); + subtitleTextView[i].setTranslationX(AndroidUtilities.lerp(0, -dp(62), transitValue)); + subtitleTextView[i].setVisibility(View.VISIBLE); + subtitleTextView[i].setAlpha(AndroidUtilities.lerp(i == 0 ? 1.0f : 0.0f, i == 1 ? 1.0f : 0.0f, transitValue)); + } + } + }); + transitionAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + transitValue = stories ? 1.0f : 0.0f; + invalidate(); + for (int i = 0; i < 2; ++i) { + titleTextView[i].setTranslationX(AndroidUtilities.lerp(0, -dp(62), transitValue)); + titleTextView[i].setVisibility((i == 1) == stories ? View.VISIBLE : View.GONE); + titleTextView[i].setAlpha(AndroidUtilities.lerp(i == 0 ? 1.0f : 0.0f, i == 1 ? 1.0f : 0.0f, transitValue)); + subtitleTextView[i].setTranslationX(AndroidUtilities.lerp(0, -dp(62), transitValue)); + subtitleTextView[i].setVisibility((i == 1) == stories ? View.VISIBLE : View.GONE); + subtitleTextView[i].setAlpha(AndroidUtilities.lerp(i == 0 ? 1.0f : 0.0f, i == 1 ? 1.0f : 0.0f, transitValue)); + } + } + }); + transitionAnimator.setDuration(320); + transitionAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + transitionAnimator.start(); } @Override protected void onDraw(Canvas canvas) { - canvas.save(); - canvas.translate(0, 0); + if (transitValue > 0) { + canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), (int) (0xFF * (1.0f - transitValue)), Canvas.ALL_SAVE_FLAG); + } else { + canvas.save(); + } + canvas.translate(AndroidUtilities.lerp(0, -dp(62), transitValue), 0); avatarsDrawable.onDraw(canvas); canvas.restore(); @@ -331,5 +463,25 @@ protected void onDraw(Canvas canvas) { if (dividerPaint == null) dividerPaint = Theme.dividerPaint; canvas.drawRect(0, getHeight() - 1, getWidth(), getHeight(), dividerPaint); } + + public static class Factory extends UItem.UItemFactory { + static { setup(new Factory()); } + + @Override + public StoriesView createView(Context context, int currentAccount, int classGuid, Theme.ResourcesProvider resourcesProvider) { + return new StoriesView(context, resourcesProvider); + } + + @Override + public void bindView(View view, UItem item, boolean divider) { + ((StoriesView) view).set((StoriesController.SearchStoriesList) item.object); + } + + public static UItem asStoriesList(StoriesController.SearchStoriesList list) { + final UItem item = UItem.ofFactory(Factory.class); + item.object = list; + return item; + } + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ArchiveSettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ArchiveSettingsActivity.java index 64d2ecbc76a..0f9a41fa62d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ArchiveSettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ArchiveSettingsActivity.java @@ -51,7 +51,7 @@ public class ArchiveSettingsActivity extends BaseFragment implements Notificatio public View createView(Context context) { actionBar.setBackButtonImage(R.drawable.ic_ab_back); actionBar.setAllowOverlayTitle(true); - actionBar.setTitle(LocaleController.getString("ArchiveSettings")); + actionBar.setTitle(LocaleController.getString(R.string.ArchiveSettings)); actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override public void onItemClick(int id) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ArchivedStickersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ArchivedStickersActivity.java index ad50ceeb387..19b59a1fa96 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ArchivedStickersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ArchivedStickersActivity.java @@ -9,7 +9,6 @@ package org.telegram.ui; import android.content.Context; -import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; @@ -148,7 +147,7 @@ public void onItemClick(int id) { inputStickerSet.short_name = stickerSet.set.short_name; } inputStickerSet.access_hash = stickerSet.set.access_hash; - final StickersAlert stickersAlert = new StickersAlert(getParentActivity(), ArchivedStickersActivity.this, inputStickerSet, null, null); + final StickersAlert stickersAlert = new StickersAlert(getParentActivity(), ArchivedStickersActivity.this, inputStickerSet, null, null, false); stickersAlert.setInstallDelegate(new StickersAlert.StickersAlertInstallDelegate() { @Override public void onStickerSetInstalled() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java index f6ac12258a8..ffbaf80eaac 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java @@ -114,6 +114,7 @@ import androidx.viewpager.widget.ViewPager; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; +import com.google.android.exoplayer2.util.Log; import org.json.JSONObject; import org.telegram.messenger.AndroidUtilities; @@ -747,7 +748,6 @@ private class WindowView extends FrameLayout { private final Paint blackPaint = new Paint(); private Runnable attachRunnable; - private boolean selfLayout; private int startedTrackingPointerId; private boolean maybeStartTracking; private boolean startedTracking; @@ -862,9 +862,6 @@ public boolean dispatchTouchEvent(MotionEvent ev) { @SuppressWarnings("DrawAllocation") @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - if (selfLayout) { - return; - } int width = right - left; if (anchorsOffsetMeasuredWidth != width) { for (int i = 0; i < pages.length; i++) { @@ -3377,7 +3374,7 @@ private void openWebpageUrl(String url, String anchor, Browser.Progress progress sheet.dismiss(true); } } - Browser.openUrl(parentActivity, Uri.parse(url), true, true, false, progress, null, true, true); + Browser.openUrl(parentActivity, Uri.parse(url), true, true, false, progress, null, true, true, false); return true; }; @@ -3660,7 +3657,7 @@ public void setParentActivity(Activity activity, BaseFragment fragment) { NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingDidReset); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingPlayStateChanged); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingDidStart); - if (parentActivity == activity) { + if (parentActivity == activity || parentActivity != null && isSheet && sheet != null && sheet.dialog != null) { updatePaintColors(); refreshThemeColors(); return; @@ -3996,8 +3993,9 @@ protected WebInstantView.Loader getInstantViewLoader() { end = Math.max(sb.getSpanEnd(spans[i]), end); } Uri uri = Utilities.uriParseSafe(url); + if (uri != null && TextUtils.equals(uri.getScheme(), "javascript")) return; if (spans.length > 0 && start == 0 && end > 0 || uri != null && uri.getScheme() != null) { - if (uri.getScheme() == null && uri.getHost() == null && uri.getPath() != null) { + if (uri != null && uri.getScheme() == null && uri.getHost() == null && uri.getPath() != null) { url = Browser.replace(uri, "https", null, uri.getPath(), "/"); } page.getWebView().loadUrl(url); @@ -8235,6 +8233,7 @@ public void fillTextLayoutBlocks(ArrayList private class BlockEmbedCell extends FrameLayout implements TextSelectionHelper.ArticleSelectableView { private class TelegramWebviewProxy { + @Keep @JavascriptInterface public void postEvent(final String eventName, final String eventData) { AndroidUtilities.runOnUIThread(() -> { @@ -12893,10 +12892,7 @@ public class PageLayout extends FrameLayout { public final BotWebViewContainer webViewContainer; private boolean swipeBack; - private int errorShownCode; - private String errorShownDescription; private boolean errorShown; - private boolean dangerousShown; public ErrorContainer errorContainer; public boolean backButton, forwardButton; @@ -13001,9 +12997,9 @@ public void setPageLoaded(String url, boolean animated) { } @Override - public void onWebViewCreated() { - super.onWebViewCreated(); - swipeContainer.setWebView(webViewContainer.getWebView()); + public void onWebViewCreated(MyWebView webView) { + super.onWebViewCreated(webView); + swipeContainer.setWebView(webView); } @Override @@ -13039,7 +13035,7 @@ protected void onFaviconChanged(Bitmap favicon) { protected void onErrorShown(boolean shown, int errorCode, String description) { if (shown) { createErrorContainer(); - errorContainer.set(getWebView() != null ? getWebView().getUrl() : null, errorShownCode = errorCode, errorShownDescription = description); + errorContainer.set(getWebView() != null ? getWebView().getUrl() : null, errorCode, description); errorContainer.setDark(AndroidUtilities.computePerceivedBrightness(getThemedColor(Theme.key_iv_background)) <= .721f, false); errorContainer.setBackgroundColor(getThemedColor(Theme.key_iv_background)); } @@ -13155,7 +13151,7 @@ public void onWebViewScrolled(WebView webView, int dx, int dy) { }); swipeContainer.addView(webViewContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); swipeContainer.setScrollEndListener(() -> webViewContainer.invalidateViewPortHeight(true)); - swipeContainer.setDelegate(() -> { + swipeContainer.setDelegate(byTap -> { if (sheet != null) { swipeBack = true; sheet.dismiss(true); @@ -13251,9 +13247,9 @@ public String getTitle() { } public int getBackgroundColor() { - if (isWeb() && dangerousShown) { - return 0xFFB3261E; - } +// if (isWeb() && dangerousShown) { +// return 0xFFB3261E; +// } if (isWeb() && SharedConfig.adaptableColorInBrowser) { if (errorShown) { return getThemedColor(Theme.key_iv_background); @@ -13634,7 +13630,7 @@ public void attach(PageLayout pageLayout) { if (webView != null) { webView.onResume(); - pageLayout.webViewContainer.replaceWebView(webView, proxy); + pageLayout.webViewContainer.replaceWebView(UserConfig.selectedAccount, webView, proxy); pageLayout.setWebBgColor(true, actionBarColor); pageLayout.setWebBgColor(false, backgroundColor); } else if (lastUrl != null) { @@ -13784,14 +13780,21 @@ public WindowView getWindowView() { public boolean preserve; - private BottomSheetTabDialog dialog; + public BottomSheetTabDialog dialog; + private boolean hadDialog; @Override public boolean setDialog(BottomSheetTabDialog dialog) { this.dialog = dialog; + if (dialog != null) hadDialog = true; return true; } + @Override + public boolean hadDialog() { + return hadDialog; + } + @Override public BottomSheetTabs.WebTabData saveState() { BottomSheetTabs.WebTabData tab = new BottomSheetTabs.WebTabData(); @@ -13822,10 +13825,11 @@ public boolean restoreState(BaseFragment fragment, BottomSheetTabs.WebTabData ta @Override public boolean isShown() { - return !dismissing && openProgress > 0.5f && windowView != null && windowView.isAttachedToWindow() && windowView.isVisible() && backProgress < 1f; + return !dismissing && !released && openProgress > 0.5f && windowView != null && windowView.isAttachedToWindow() && windowView.isVisible() && backProgress < 1f; } public void attachInternal(BaseFragment fragment) { + this.released = false; this.fragment = fragment; this.resourcesProvider = fragment.getResourceProvider(); if (fragment instanceof ChatActivity) { @@ -13867,6 +13871,7 @@ public void dismiss() { private boolean dismissing; private boolean dismissingIntoTabs; + private boolean released; @Override public void dismiss(boolean tabs) { @@ -13887,6 +13892,7 @@ public void dismiss(boolean tabs) { @Override public void release() { + released = true; if (pages[0] != null && pages[0].swipeBack) { pages[0].swipeContainer.setSwipeOffsetY(-pages[0].swipeContainer.offsetY + pages[0].swipeContainer.topActionBarOffsetY); pages[0].swipeBack = false; @@ -14023,6 +14029,7 @@ public void reset() { checkFullyVisible(); updateTranslation(); windowView.invalidate(); + windowView.requestLayout(); } private float openProgress; @@ -14464,7 +14471,7 @@ public static class ErrorContainer extends FrameLayout { private final TextView titleView; private final TextView descriptionView; private final TextView codeView; - private final ButtonWithCounterView buttonView; + public final ButtonWithCounterView buttonView; public ErrorContainer(Context context) { super(context); @@ -14528,13 +14535,19 @@ public void setDark(boolean dark, boolean animated) { } } - public void set(String url, int code, String descrpiption) { + public void set(String botName, String description) { + titleView.setText(getString(R.string.WebErrorTitle)); + descriptionView.setText(AndroidUtilities.replaceTags(formatString(R.string.WebErrorInfoBot, botName))); + codeView.setText(description); + } + + public void set(String url, int code, String description) { titleView.setText(getString(R.string.WebErrorTitle)); url = BotWebViewContainer.magic2tonsite(url); CharSequence cs = AndroidUtilities.replaceTags(url == null || Uri.parse(url) == null || Uri.parse(url).getAuthority() == null ? getString(R.string.WebErrorInfo) : formatString(R.string.WebErrorInfoDomain, Uri.parse(url).getAuthority())); cs = Emoji.replaceEmoji(cs, descriptionView.getPaint().getFontMetricsInt(), false); descriptionView.setText(cs); - codeView.setText(descrpiption); + codeView.setText(description); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/AccountSelectCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/AccountSelectCell.java index a3a9a8b761d..a572c3b0e08 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/AccountSelectCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/AccountSelectCell.java @@ -9,6 +9,8 @@ package org.telegram.ui.Cells; +import static org.telegram.messenger.AndroidUtilities.dp; + import android.content.Context; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; @@ -45,11 +47,12 @@ public class AccountSelectCell extends FrameLayout { public AccountSelectCell(Context context, boolean hasInfo) { super(context); + setMinimumWidth(dp(196)); avatarDrawable = new AvatarDrawable(); - avatarDrawable.setTextSize(AndroidUtilities.dp(12)); + avatarDrawable.setTextSize(dp(12)); imageView = new BackupImageView(context); - imageView.setRoundRadius(AndroidUtilities.dp(18)); + imageView.setRoundRadius(dp(18)); addView(imageView, LayoutHelper.createFrame(36, 36, Gravity.LEFT | Gravity.TOP, 10, 10, 0, 0)); textView = new SimpleTextView(context); @@ -70,7 +73,7 @@ public AccountSelectCell(Context context, boolean hasInfo) { infoTextView.setLines(1); infoTextView.setMaxLines(1); infoTextView.setSingleLine(true); - infoTextView.setMaxWidth(AndroidUtilities.dp(320)); + infoTextView.setMaxWidth(dp(320)); infoTextView.setGravity(Gravity.LEFT | Gravity.TOP); infoTextView.setEllipsize(TextUtils.TruncateAt.END); addView(infoTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 61, 27, 8, 0)); @@ -86,12 +89,21 @@ public AccountSelectCell(Context context, boolean hasInfo) { } } + private int width() { + return (int) Math.max(dp(196), dp(61 + 8 + (checkImageView != null ? 50 : 0)) + Math.max( + textView.getTextPaint().measureText(textView.getText().toString()), + (infoTextView != null ? infoTextView.getPaint().measureText(infoTextView.getText().toString()) : 0) + )); + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (checkImageView != null || infoTextView != null && getLayoutParams().width != LayoutHelper.WRAP_CONTENT) { - super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(56), MeasureSpec.EXACTLY)); + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(dp(56), MeasureSpec.EXACTLY)); + } else if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) { + super.onMeasure(MeasureSpec.makeMeasureSpec(width(), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(dp(56), MeasureSpec.EXACTLY)); } else { - super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(56), MeasureSpec.EXACTLY)); + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(dp(56), MeasureSpec.EXACTLY)); } } 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 f72ffe4a8a6..10cdc5ff853 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java @@ -824,11 +824,15 @@ public void setMessageObject(MessageObject messageObject, boolean force) { } private float getUploadingInfoProgress(MessageObject messageObject) { - if (messageObject != null && messageObject.type == MessageObject.TYPE_ACTION_WALLPAPER) { - MessagesController messagesController = MessagesController.getInstance(currentAccount); - if (messagesController.uploadingWallpaper != null && TextUtils.equals(messageObject.messageOwner.action.wallpaper.uploadingImage, messagesController.uploadingWallpaper)) { - return messagesController.uploadingWallpaperInfo.uploadingProgress; + try { + if (messageObject != null && messageObject.type == MessageObject.TYPE_ACTION_WALLPAPER) { + MessagesController messagesController = MessagesController.getInstance(currentAccount); + if (messagesController.uploadingWallpaper != null && TextUtils.equals(messageObject.messageOwner.action.wallpaper.uploadingImage, messagesController.uploadingWallpaper)) { + return messagesController.uploadingWallpaperInfo.uploadingProgress; + } } + } catch (Exception e) { + FileLog.e(e); } return 1; } @@ -1549,7 +1553,13 @@ false, getString(R.string.ActionGiftStarsView), if (action.converted) { title = formatPluralStringComma("Gift2ActionConvertedInfo", (int) stars); } else if (action.saved) { - title = getString(R.string.Gift2ActionSavedInfo); + if (action.convert_stars <= 0) { + title = getString(R.string.Gift2ActionBotSavedInfo); + } else { + title = getString(R.string.Gift2ActionSavedInfo); + } + } else if (action.convert_stars <= 0) { + title = getString(R.string.Gift2ActionBotInfo); } else { title = formatPluralStringComma("Gift2ActionInfo", (int) stars); } @@ -2713,4 +2723,8 @@ private ColorFilter getAdaptiveEmojiColorFilter(int color) { } return adaptiveEmojiColorFilter; } + + public int measuredWidth() { + return getMeasuredWidth(); + } } 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 84f5c459142..df30207657c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java @@ -123,6 +123,7 @@ import org.telegram.messenger.Utilities; import org.telegram.messenger.WebFile; import org.telegram.messenger.browser.Browser; +import org.telegram.messenger.video.OldVideoPlayerRewinder; import org.telegram.messenger.video.VideoPlayerRewinder; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLObject; @@ -401,9 +402,9 @@ public void setScrimReaction(Integer scrimViewReaction) { reactionsLayoutInBubble.setScrimReaction(scrimViewReaction); } - public void drawScrimReaction(Canvas canvas, Integer scrimViewReaction, float progress) { + public void drawScrimReaction(Canvas canvas, Integer scrimViewReaction, float progress, boolean direction) { if ((currentPosition == null || ((currentPosition.flags & MessageObject.POSITION_FLAG_BOTTOM) != 0 && (currentPosition.flags & MessageObject.POSITION_FLAG_LEFT) != 0)) && !reactionsLayoutInBubble.isSmall) { - reactionsLayoutInBubble.setScrimProgress(progress); + reactionsLayoutInBubble.setScrimProgress(progress, direction); reactionsLayoutInBubble.draw(canvas, transitionParams.animateChangeProgress, scrimViewReaction); } } @@ -510,7 +511,7 @@ default void didPressCancelSendButton(ChatMessageCell cell) { default void didLongPress(ChatMessageCell cell, float x, float y) { } - default void didPressReplyMessage(ChatMessageCell cell, int id) { + default void didPressReplyMessage(ChatMessageCell cell, int id, float x, float y, boolean longpress) { } default boolean isProgressLoading(ChatMessageCell cell, int type) { @@ -618,6 +619,10 @@ default boolean canPerformActions() { return false; } + default boolean canPerformReply() { + return canPerformActions(); + } + default boolean onAccessibilityAction(int action, Bundle arguments) { return false; } @@ -1573,13 +1578,12 @@ public void run() { private Theme.MessageDrawable.PathDrawParams backgroundCacheParams = new Theme.MessageDrawable.PathDrawParams(); VideoForwardDrawable videoForwardDrawable; - VideoPlayerRewinder videoPlayerRewinder; + OldVideoPlayerRewinder videoPlayerRewinder; private Theme.ResourcesProvider resourcesProvider; private final boolean canDrawBackgroundInParent; private ChatMessageSharedResources sharedResources; - // Public for enter transition public List replySpoilers = new ArrayList<>(); private final Stack replySpoilersPool = new Stack<>(); private final Path sPath = new Path(); @@ -3842,7 +3846,9 @@ private boolean checkBotButtonMotionEvent(MotionEvent event) { if (!currentMessageObject.scheduled) { if (button2.button != null) { cancelCheckLongPress(); - delegate.didLongPressBotButton(this, button2.button); + if (delegate != null) { + delegate.didLongPressBotButton(this, button2.button); + } } } } @@ -3865,7 +3871,9 @@ private boolean checkBotButtonMotionEvent(MotionEvent event) { Toast.makeText(getContext(), getString(R.string.MessageScheduledBotAction), Toast.LENGTH_LONG).show(); } else { if (button.button != null) { - delegate.didPressBotButton(this, button.button); + if (delegate != null) { + delegate.didPressBotButton(this, button.button); + } } } pressedBotButton = -1; @@ -3885,14 +3893,121 @@ private boolean checkBotButtonMotionEvent(MotionEvent event) { return result; } + private boolean checkReplyTouchEvent(MotionEvent event) { + if (replyNameLayout == null || delegate == null || !delegate.canPerformReply()) return false; + float x = event.getX(); + float y = event.getY(); + int replyEnd; + if (currentMessageObject.shouldDrawWithoutBackground()) { + replyEnd = replyStartX + Math.max(replyNameWidth, replyTextWidth); + } else { + replyEnd = replyStartX + backgroundDrawableRight; + } + if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (x >= replyStartX && x <= replyEnd && y >= replyStartY && y <= replyStartY + replyHeight) { + replyPressed = true; + getParent().requestDisallowInterceptTouchEvent(false); + replyTouchX = x; + replyTouchY = y + getY(); + if (replySelector != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + replySelector.setHotspot(x, y); + } + replySelectorPressed = false; + replySelectorCanBePressed = true; + postDelayed(() -> { + if (replyPressed && !replySelectorPressed && replySelectorCanBePressed) { + replySelectorPressed = true; + replySelector.setState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled}); + } + }, ViewConfiguration.getTapTimeout() / 6); + invalidate(); + } + if (replyBounce != null) { + replyBounce.setPressed(true); + replyBounceX = x; + replyBounceY = y; + } + startCheckLongPress(); + return true; + } + } else if (event.getAction() == MotionEvent.ACTION_UP) { + if (replyPressed) { + cancelCheckLongPress(); + + replyPressed = false; + if (replySelector != null) { + if (!replySelectorPressed) { + replySelector.setState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled}); + post(() -> { + replySelector.setState(new int[]{}); + invalidate(); + }); + } else { + replySelector.setState(new int[]{}); + } + replySelectorPressed = false; + replySelectorCanBePressed = false; + invalidate(); + } + if (replyBounce != null) { + replyBounce.setPressed(false); + } + playSoundEffect(SoundEffectConstants.CLICK); + if (replyPanelIsForward) { + if (delegate != null) { + if (currentForwardChannel != null) { + delegate.didPressChannelAvatar(this, currentForwardChannel, currentMessageObject.messageOwner.fwd_from.channel_post, lastTouchX, lastTouchY, false); + } else if (currentForwardUser != null) { + delegate.didPressUserAvatar(this, currentForwardUser, lastTouchX, lastTouchY, false); + } else if (currentForwardName != null) { + delegate.didPressHiddenForward(this); + } + } + } else { + if (delegate != null && (currentMessageObject.hasValidReplyMessageObject() || currentMessageObject.isReplyToStory() || hasReplyQuote || currentMessageObject.messageOwner != null && currentMessageObject.messageOwner.reply_to != null && currentMessageObject.messageOwner.reply_to.reply_from != null)) { + delegate.didPressReplyMessage(this, currentMessageObject.getReplyMsgId(), x, y, false); + } + } + } + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + replyPressed = false; + replySelectorPressed = false; + if (replySelector != null) { + replySelector.setState(new int[]{}); + } + invalidate(); + if (replyBounce != null) { + replyBounce.setPressed(false); + } + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (!(x >= replyStartX && x <= replyEnd && y >= replyStartY && y <= replyStartY + replyHeight)) { + replyPressed = false; + replySelectorPressed = false; + replySelectorCanBePressed = false; + if (replySelector != null) { + replySelector.setState(new int[]{}); + } + invalidate(); + if (replyBounce != null) { + replyBounce.setPressed(false); + } + } else if (replySelector != null && replySelectorCanBePressed && Math.sqrt(Math.pow(x - replyTouchX, 2) + Math.pow((y + getY()) - replyTouchY, 2)) > 0.75f) { + replySelectorCanBePressed = false; + } + } + return replyPressed; + } + @Override public boolean onTouchEvent(MotionEvent event) { if (currentMessageObject == null || delegate != null && !delegate.canPerformActions() || animationRunning) { if (currentMessageObject != null && currentMessageObject.preview) { return checkTextSelection(event); } else { - checkTextSelection(event); - return super.onTouchEvent(event); + boolean r_reply = checkReplyTouchEvent(event); + boolean r_text = r_reply || checkTextSelection(event); + return r_reply || super.onTouchEvent(event); } } @@ -4011,6 +4126,9 @@ public boolean onTouchEvent(MotionEvent event) { if (!result && groupMedia != null) { result = groupMedia.onTouchEvent(event); } + if (!result) { + result = checkReplyTouchEvent(event); + } if (event.getAction() == MotionEvent.ACTION_CANCEL) { spoilerPressed = null; @@ -4127,38 +4245,6 @@ public boolean onTouchEvent(MotionEvent event) { } result = true; invalidate(); - } else if (replyNameLayout != null) { - int replyEnd; - if (currentMessageObject.shouldDrawWithoutBackground()) { - replyEnd = replyStartX + Math.max(replyNameWidth, replyTextWidth); - } else { - replyEnd = replyStartX + backgroundDrawableRight; - } - if (x >= replyStartX && x <= replyEnd && y >= replyStartY && y <= replyStartY + replyHeight) { - replyPressed = true; - replyTouchX = x; - replyTouchY = y + getY(); - if (replySelector != null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - replySelector.setHotspot(x, y); - } - replySelectorPressed = false; - replySelectorCanBePressed = true; - postDelayed(() -> { - if (replyPressed && !replySelectorPressed && replySelectorCanBePressed) { - replySelectorPressed = true; - replySelector.setState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled}); - } - }, ViewConfiguration.getTapTimeout() / 6); - invalidate(); - } - if (replyBounce != null) { - replyBounce.setPressed(true); - replyBounceX = x; - replyBounceY = y; - } - result = true; - } } if (result) { startCheckLongPress(); @@ -4271,74 +4357,6 @@ public boolean onTouchEvent(MotionEvent event) { } } } - } else if (replyPressed) { - if (event.getAction() == MotionEvent.ACTION_UP) { - replyPressed = false; - if (replySelector != null) { - if (!replySelectorPressed) { - replySelector.setState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled}); - post(() -> { - replySelector.setState(new int[]{}); - invalidate(); - }); - } else { - replySelector.setState(new int[]{}); - } - replySelectorPressed = false; - replySelectorCanBePressed = false; - invalidate(); - } - if (replyBounce != null) { - replyBounce.setPressed(false); - } - playSoundEffect(SoundEffectConstants.CLICK); - if (replyPanelIsForward) { - if (delegate != null) { - if (currentForwardChannel != null) { - delegate.didPressChannelAvatar(this, currentForwardChannel, currentMessageObject.messageOwner.fwd_from.channel_post, lastTouchX, lastTouchY, false); - } else if (currentForwardUser != null) { - delegate.didPressUserAvatar(this, currentForwardUser, lastTouchX, lastTouchY, false); - } else if (currentForwardName != null) { - delegate.didPressHiddenForward(this); - } - } - } else { - if (delegate != null && (currentMessageObject.hasValidReplyMessageObject() || currentMessageObject.isReplyToStory() || hasReplyQuote || currentMessageObject.messageOwner != null && currentMessageObject.messageOwner.reply_to != null && currentMessageObject.messageOwner.reply_to.reply_from != null)) { - delegate.didPressReplyMessage(this, currentMessageObject.getReplyMsgId()); - } - } - } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { - replyPressed = false; - replySelectorPressed = false; - if (replySelector != null) { - replySelector.setState(new int[]{}); - } - invalidate(); - if (replyBounce != null) { - replyBounce.setPressed(false); - } - } else if (event.getAction() == MotionEvent.ACTION_MOVE) { - int replyEnd; - if (currentMessageObject.shouldDrawWithoutBackground()) { - replyEnd = replyStartX + Math.max(replyNameWidth, replyTextWidth); - } else { - replyEnd = replyStartX + backgroundDrawableRight; - } - if (!(x >= replyStartX && x <= replyEnd && y >= replyStartY && y <= replyStartY + replyHeight)) { - replyPressed = false; - replySelectorPressed = false; - replySelectorCanBePressed = false; - if (replySelector != null) { - replySelector.setState(new int[]{}); - } - invalidate(); - if (replyBounce != null) { - replyBounce.setPressed(false); - } - } else if (replySelector != null && replySelectorCanBePressed && Math.sqrt(Math.pow(x - replyTouchX, 2) + Math.pow((y + getY()) - replyTouchY, 2)) > 0.75f) { - replySelectorCanBePressed = false; - } - } } else if (sideButtonPressed) { if (event.getAction() == MotionEvent.ACTION_UP) { playSoundEffect(SoundEffectConstants.CLICK); @@ -4896,8 +4914,8 @@ private void didClickedImage() { } else if (buttonState == 2 || buttonState == 0) { didPressButton(true, false); } - } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO) { - if (buttonState == -1 || drawVideoImageButton && (autoPlayingMedia || SharedConfig.streamMedia && canStreamVideo)) { + } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO || currentMessageObject.hasVideoQualities()) { + if (buttonState == -1 || drawVideoImageButton && (autoPlayingMedia || currentMessageObject != null && currentMessageObject.hasVideoQualities() || SharedConfig.streamMedia && canStreamVideo)) { delegate.didPressImage(this, lastTouchX, lastTouchY); } else if (drawVideoImageButton) { didPressButton(true, true); @@ -5455,6 +5473,14 @@ private void fileAttach(boolean checkUI, final MessageObject messageObject) { updateButtonState(false, false, false); } } + if (messageObject.hasVideoQualities()) { + if (messageObject.highestQuality != null && !messageObject.highestQuality.isManifestCached()) { + FileLoader.getInstance(currentAccount).loadFile(messageObject.highestQuality.manifestDocument, messageObject, FileLoader.PRIORITY_NORMAL, 0); + } + if (messageObject.thumbQuality != null && !messageObject.thumbQuality.isManifestCached()) { + FileLoader.getInstance(currentAccount).loadFile(messageObject.thumbQuality.manifestDocument, messageObject, FileLoader.PRIORITY_NORMAL, 0); + } + } } } @@ -9143,7 +9169,10 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe photoImage.startAnimation(); TLRPC.Document document = messageObject.getDocument(); if (messageObject.hasVideoQualities()) { - document = VideoPlayer.getDocumentForThumb(currentAccount, MessageObject.getMedia(messageObject)); + VideoPlayer.VideoUri uri = VideoPlayer.getQualityForThumb(messageObject.videoQualities); + if (uri != null) { + document = uri.document; + } } if (currentMessageObject.videoEditedInfo != null && currentMessageObject.videoEditedInfo.canAutoPlaySourceVideo() && document != null) { @@ -9641,42 +9670,40 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe substractBackgroundHeight = 0; keyboardHeight = 0; } - //if (expiredStoryView == null || !expiredStoryView.visible) { - if (drawCommentButton) { - totalHeight += AndroidUtilities.dp(shouldDrawTimeOnMedia() ? 41.3f : 43); - createSelectorDrawable(1); - } - if (hasFactCheck && currentPosition != null) { - totalHeight += dp(2 + (reactionsLayoutInBubble.isEmpty ? 18 : 0)) + factCheckHeight; - } - if (drawPinnedBottom && drawPinnedTop) { + if (drawCommentButton) { + totalHeight += AndroidUtilities.dp(shouldDrawTimeOnMedia() ? 41.3f : 43); + createSelectorDrawable(1); + } + if (hasFactCheck && currentPosition != null) { + totalHeight += dp(2 + (reactionsLayoutInBubble.isEmpty ? 18 : 0)) + factCheckHeight; + } + if (drawPinnedBottom && drawPinnedTop) { + totalHeight -= AndroidUtilities.dp(2); + } else if (drawPinnedBottom) { + totalHeight -= AndroidUtilities.dp(1); + } else if (drawPinnedTop && pinnedBottom && currentPosition != null && currentPosition.siblingHeights == null) { + totalHeight -= AndroidUtilities.dp(1); + } + if (!mediaBackground) { + if (messageObject.type == MessageObject.TYPE_TEXT) { totalHeight -= AndroidUtilities.dp(2); - } else if (drawPinnedBottom) { - totalHeight -= AndroidUtilities.dp(1); - } else if (drawPinnedTop && pinnedBottom && currentPosition != null && currentPosition.siblingHeights == null) { + } + if (drawPinnedBottom) { totalHeight -= AndroidUtilities.dp(1); } - if (!mediaBackground) { - if (messageObject.type == MessageObject.TYPE_TEXT) { - totalHeight -= AndroidUtilities.dp(2); - } - if (drawPinnedBottom) { - totalHeight -= AndroidUtilities.dp(1); - } - if (drawPinnedTop) { - totalHeight -= AndroidUtilities.dp(1); - } + if (drawPinnedTop) { + totalHeight -= AndroidUtilities.dp(1); } - if (messageObject.type != MessageObject.TYPE_EMOJIS) { - if (messageObject.isAnyKindOfSticker() && totalHeight < AndroidUtilities.dp(70)) { - additionalTimeOffsetY = AndroidUtilities.dp(70) - totalHeight; - totalHeight += additionalTimeOffsetY; - } else if (messageObject.isAnimatedEmoji()) { - additionalTimeOffsetY = AndroidUtilities.dp(16); - totalHeight += AndroidUtilities.dp(16); - } + } + if (messageObject.type != MessageObject.TYPE_EMOJIS) { + if (messageObject.isAnyKindOfSticker() && totalHeight < AndroidUtilities.dp(70)) { + additionalTimeOffsetY = AndroidUtilities.dp(70) - totalHeight; + totalHeight += additionalTimeOffsetY; + } else if (messageObject.isAnimatedEmoji()) { + additionalTimeOffsetY = AndroidUtilities.dp(16); + totalHeight += AndroidUtilities.dp(16); } - // } + } if (!drawPhotoImage) { photoImage.setImageBitmap((Drawable) null); @@ -9872,7 +9899,7 @@ private void updateFlagSecure() { Activity activity = AndroidUtilities.findActivity(getContext()); Window window = activity == null ? null : activity.getWindow(); if (window != null) { - flagSecure = new FlagSecureReason(window, () -> currentMessageObject != null && currentMessageObject.messageOwner != null && (currentMessageObject.type == MessageObject.TYPE_PAID_MEDIA && (groupMedia == null || groupMedia.hidden) || currentMessageObject.messageOwner.noforwards || currentMessageObject.isVoiceOnce() || currentMessageObject.hasRevealedExtendedMedia())); + flagSecure = new FlagSecureReason(window, () -> currentMessageObject != null && currentMessageObject.messageOwner != null && (currentMessageObject.type == MessageObject.TYPE_PAID_MEDIA && (groupMedia == null || !groupMedia.hidden) || currentMessageObject.messageOwner.noforwards || currentMessageObject.isVoiceOnce() || currentMessageObject.hasRevealedExtendedMedia())); if (attachedToWindow) { flagSecure.attach(); } @@ -9926,7 +9953,7 @@ protected boolean onLongPress() { boolean forward = lastTouchX > photoImage.getCenterX(); if (videoPlayerRewinder == null) { videoForwardDrawable = new VideoForwardDrawable(true); - videoPlayerRewinder = new VideoPlayerRewinder() { + videoPlayerRewinder = new OldVideoPlayerRewinder() { @Override protected void onRewindCanceled() { onTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0)); @@ -10055,6 +10082,32 @@ public void invalidate() { pressedBotButton = -1; invalidate(); } + if (replyPressed && !replyPanelIsForward) { + hadLongPress = true; + replyPressed = false; + if (replySelector != null) { + if (!replySelectorPressed) { + replySelector.setState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled}); + post(() -> { + replySelector.setState(new int[]{}); + invalidate(); + }); + } else { + replySelector.setState(new int[]{}); + } + replySelectorPressed = false; + replySelectorCanBePressed = false; + invalidate(); + } + if (replyBounce != null) { + replyBounce.setPressed(false); + } + playSoundEffect(SoundEffectConstants.CLICK); + if (delegate != null && (currentMessageObject.hasValidReplyMessageObject() || currentMessageObject.isReplyToStory() || hasReplyQuote || currentMessageObject.messageOwner != null && currentMessageObject.messageOwner.reply_to != null && currentMessageObject.messageOwner.reply_to.reply_from != null)) { + delegate.didPressReplyMessage(this, currentMessageObject.getReplyMsgId(), replyBounceX, replyBounceY, true); + } + return true; + } linkPreviewPressed = false; sideButtonPressed = false; @@ -10405,6 +10458,8 @@ private void updateWaveform() { ( UserConfig.getInstance(currentAccount).isPremium() || + TranscribeButton.isFreeTranscribeInChat(currentMessageObject) + || MessagesController.getInstance(currentAccount).transcribeAudioTrialWeeklyNumber > 0 && currentMessageObject.getDuration() <= MessagesController.getInstance(currentAccount).transcribeAudioTrialDurationMax && ( currentMessageObject.messageOwner != null && ( @@ -14748,7 +14803,7 @@ private int getIconForCurrentState() { } } else if (buttonState == -1) { if (documentAttachType == DOCUMENT_ATTACH_TYPE_DOCUMENT) { - return (drawPhotoImage && (currentPhotoObject != null || currentPhotoObjectThumb != null) && (photoImage.hasBitmapImage() || currentMessageObject.mediaExists || currentMessageObject.attachPathExists)) ? MediaActionDrawable.ICON_NONE : MediaActionDrawable.ICON_FILE; + return (drawPhotoImage && (currentPhotoObject != null || currentPhotoObjectThumb != null) && (photoImage.hasBitmapImage() || currentMessageObject.mediaExists() || currentMessageObject.attachPathExists)) ? MediaActionDrawable.ICON_NONE : MediaActionDrawable.ICON_FILE; } else if (currentMessageObject.needDrawBluredPreview()) { return MediaActionDrawable.ICON_FIRE; } else if (hasEmbed) { @@ -14830,7 +14885,7 @@ public void updateButtonState(boolean ifSame, boolean animated, boolean fromSet) return; } fileName = FileLoader.getAttachFileName(currentPhotoObject); - fileExists = currentMessageObject.mediaExists; + fileExists = currentMessageObject.mediaExists(); } else if ( currentMessageObject.type == MessageObject.TYPE_GIF || documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND || @@ -14850,14 +14905,15 @@ public void updateButtonState(boolean ifSame, boolean animated, boolean fromSet) fileExists = true; } else if (!currentMessageObject.isSendError() || documentAttachType == DOCUMENT_ATTACH_TYPE_AUDIO || documentAttachType == DOCUMENT_ATTACH_TYPE_MUSIC) { fileName = currentMessageObject.getFileName(); - fileExists = currentMessageObject.mediaExists; +// currentMessageObject.updateQualitiesCached(true); + fileExists = currentMessageObject.mediaExists(); } } else if (documentAttachType != DOCUMENT_ATTACH_TYPE_NONE) { fileName = FileLoader.getAttachFileName(documentAttach); - fileExists = currentMessageObject.mediaExists; + fileExists = currentMessageObject.mediaExists(); } else if (currentPhotoObject != null) { fileName = FileLoader.getAttachFileName(currentPhotoObject); - fileExists = currentMessageObject.mediaExists; + fileExists = currentMessageObject.mediaExists(); } boolean autoDownload; @@ -14945,7 +15001,7 @@ public void updateButtonState(boolean ifSame, boolean animated, boolean fromSet) buttonState = 1; } radialProgress.setIcon(getIconForCurrentState(), ifSame, animated); - if (hasMiniProgress == 1 || currentMessageObject != null && currentMessageObject.hasVideoQualities()) { + if (hasMiniProgress == 1) { DownloadController.getInstance(currentAccount).removeLoadingFileObserver(this); miniButtonState = -1; } else { @@ -15119,7 +15175,7 @@ public void updateButtonState(boolean ifSame, boolean animated, boolean fromSet) radialProgress.setMiniProgressBackgroundColor(getThemedColor(Theme.key_chat_inLoaderPhoto)); buttonState = 3; radialProgress.setIcon(getIconForCurrentState(), ifSame, animated); - if (hasMiniProgress == 1 || currentMessageObject.sendPreview || currentMessageObject != null && currentMessageObject.hasVideoQualities()) { + if (hasMiniProgress == 1 || currentMessageObject.sendPreview) { DownloadController.getInstance(currentAccount).removeLoadingFileObserver(this); miniButtonState = -1; } else { @@ -15855,7 +15911,7 @@ private void measureTime(MessageObject messageObject) { } if (messageObject.getDialogId() < 0) { TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-messageObject.getDialogId()); - if (chat != null && chat.signature_profiles) { + if (chat != null && chat.signature_profiles && (messageObject.messageOwner.flags & 256) != 0) { signString = null; } } @@ -15891,7 +15947,7 @@ private void measureTime(MessageObject messageObject) { } else if (currentMessageObject.isRepostPreview) { timeString = LocaleController.formatSmallDateChat(messageObject.messageOwner.date) + ", " + LocaleController.getInstance().getFormatterDay().format((long) (messageObject.messageOwner.date) * 1000); } else if (edited) { - timeString = getString("EditedMessage", R.string.EditedMessage) + " " + LocaleController.getInstance().getFormatterDay().format((long) (messageObject.messageOwner.date) * 1000); + timeString = getString(R.string.EditedMessage) + " " + LocaleController.getInstance().getFormatterDay().format((long) (messageObject.messageOwner.date) * 1000); } else if (currentMessageObject.isSaved && currentMessageObject.messageOwner.fwd_from != null && (currentMessageObject.messageOwner.fwd_from.date != 0 || currentMessageObject.messageOwner.fwd_from.saved_date != 0)) { int date = currentMessageObject.messageOwner.fwd_from.saved_date; if (date == 0) { @@ -15901,6 +15957,9 @@ private void measureTime(MessageObject messageObject) { } else { timeString = LocaleController.getInstance().getFormatterDay().format((long) (messageObject.messageOwner.date) * 1000); } + if (currentMessageObject.messageOwner.video_processing_pending) { + timeString = LocaleController.formatString(R.string.ScheduledTimeApprox, timeString); + } if (signString != null) { if (messageObject.messageOwner.via_business_bot_id != 0) { currentTimeString = timeString + ", "; @@ -16030,7 +16089,7 @@ private boolean hasSelectionOverlay() { return selectionOverlayColor != 0 && selectionOverlayColor != 0xffff0000; } - private boolean isDrawSelectionBackground() { + public boolean isDrawSelectionBackground() { return (isPressed() && isCheckPressed || !isCheckPressed && isPressed || isHighlighted) && !textIsSelectionMode() && !hasSelectionOverlay() && (currentMessageObject == null || !currentMessageObject.preview); } @@ -16231,7 +16290,7 @@ private void setMessageObjectInternal(MessageObject messageObject) { } else { color = getThemedColor(currentMessageObject.isOutOwner() ? Theme.key_chat_outViaBotNameText : Theme.key_chat_inViaBotNameText); } - String viaBotString = getString("ViaBot", R.string.ViaBot); + String viaBotString = getString(R.string.ViaBot); if (currentNameString.length() > 0) { SpannableStringBuilder stringBuilder = new SpannableStringBuilder(); stringBuilder.append(nameStringFinal).append(" ").append(viaBotString).append(" ").append(viaUsername); @@ -19940,10 +19999,12 @@ public void drawReactionsLayout(Canvas canvas, float alpha, Integer only) { restore = true; } if (reactionsLayoutInBubble.drawServiceShaderBackground > 0 || !transitionParams.animateBackgroundBoundsInner || currentPosition != null || isRoundVideo) { + reactionsLayoutInBubble.setScrimProgress(0, false); reactionsLayoutInBubble.draw(canvas, transitionParams.animateChange ? transitionParams.animateChangeProgress : 1f, only); } else { canvas.save(); canvas.clipRect(0, 0, getMeasuredWidth(), getBackgroundDrawableBottom() + transitionParams.deltaBottom); + reactionsLayoutInBubble.setScrimProgress(0, false); reactionsLayoutInBubble.draw(canvas, transitionParams.animateChange ? transitionParams.animateChangeProgress : 1f, only); canvas.restore(); } @@ -20500,6 +20561,7 @@ private void drawTimeInternal(Canvas canvas, float alpha, boolean fromParent, fl float additionalX = -timeLayout.getLineLeft(0) + (currentMessageObject != null && currentMessageObject.isAnyKindOfSticker() ? dp(-STICKER_STATUS_OFFSET) : 0); if (currentMessageObject.shouldDrawReactions() && reactionsLayoutInBubble.isSmall) { updateReactionLayoutPosition(); + reactionsLayoutInBubble.setScrimProgress(0, false); reactionsLayoutInBubble.draw(canvas, transitionParams.animateChangeProgress, null); } @@ -20571,6 +20633,7 @@ private void drawTimeInternal(Canvas canvas, float alpha, boolean fromParent, fl float additionalX = -timeLayout.getLineLeft(0); if (currentMessageObject.shouldDrawReactions() && reactionsLayoutInBubble.isSmall) { updateReactionLayoutPosition(); + reactionsLayoutInBubble.setScrimProgress(0, false); reactionsLayoutInBubble.draw(canvas, transitionParams.animateChangeProgress, null); } if (ChatObject.isChannel(currentChat) && !currentChat.megagroup || (currentMessageObject.messageOwner.flags & TLRPC.MESSAGE_FLAG_HAS_VIEWS) != 0 || (repliesLayout != null || transitionParams.animateReplies) || (isPinned || transitionParams.animatePinned)) { @@ -21376,10 +21439,9 @@ public void drawOverlays(Canvas canvas) { loadingProgressAlpha = animatingLoadingProgressProgress; } else { drawLoadingProgress = (buttonState == 1 || miniButtonState == 1 || animatingLoadingProgressProgress != 0) && !currentMessageObject.isSecretMedia() && - (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO || documentAttachType == DOCUMENT_ATTACH_TYPE_GIF || documentAttachType == DOCUMENT_ATTACH_TYPE_DOCUMENT) && - !currentMessageObject.hasVideoQualities(); + (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO || documentAttachType == DOCUMENT_ATTACH_TYPE_GIF || documentAttachType == DOCUMENT_ATTACH_TYPE_DOCUMENT); if (currentMessageObject.type == MessageObject.TYPE_VIDEO || currentMessageObject.type == MessageObject.TYPE_GIF || documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO) { - alpha = currentMessageObject.needDrawBluredPreview() && docTitleLayout == null || currentMessageObject.hasVideoQualities() ? 0 : animatingDrawVideoImageButtonProgress; + alpha = currentMessageObject.needDrawBluredPreview() && docTitleLayout == null ? 0 : animatingDrawVideoImageButtonProgress; } drawDocTitleLayout = alpha > 0 && docTitleLayout != null; if (!drawDocTitleLayout && (drawLoadingProgress || infoLayout == null)) { @@ -22066,7 +22128,7 @@ public void drawOverlays(Canvas canvas) { } boolean restore = false; boolean on = false; - if (currentMessageObject != null && currentMessageObject.isRoundVideo() && (!currentMessageObject.mediaExists || currentMessageObject.isRoundOnce())) { + if (currentMessageObject != null && currentMessageObject.isRoundVideo() && (!currentMessageObject.mediaExists() || currentMessageObject.isRoundOnce())) { radialProgress.setProgressRect( photoImage.getImageX() + (photoImage.getImageWidth() / 2f - radialProgress.getRadius()), photoImage.getImageY() + (photoImage.getImageHeight() / 2f - radialProgress.getRadius()), @@ -22130,7 +22192,7 @@ public void drawOverlays(Canvas canvas) { invalidate(); updateSecretTimeText(currentMessageObject); } - if ((drawVideoImageButton || animatingDrawVideoImageButton != 0) && !currentMessageObject.hasVideoQualities() && !currentMessageObject.isRepostPreview && !currentMessageObject.sendPreview && photoImage.getVisible() && !isSmallImage && !currentMessageObject.isHiddenSensitive()) { + if ((drawVideoImageButton || animatingDrawVideoImageButton != 0) && !currentMessageObject.isRepostPreview && !currentMessageObject.sendPreview && photoImage.getVisible() && !isSmallImage && !currentMessageObject.isHiddenSensitive()) { float alpha = controlsAlpha; if (drawPhotoImage && currentMessageObject != null && currentMessageObject.hasMediaSpoilers() && currentMessageObject.isSensitive() && (!currentMessageObject.isMediaSpoilersRevealed || mediaSpoilerRevealProgress != 0 && mediaSpoilerRevealProgress < 1)) { alpha *= mediaSpoilerRevealProgress; @@ -23651,7 +23713,7 @@ public boolean performAction(int virtualViewId, int action, Bundle arguments) { } } else if (virtualViewId == REPLY) { if (delegate != null && (!isThreadChat || currentMessageObject.getReplyTopMsgId() != 0) && (currentMessageObject.hasValidReplyMessageObject() || hasReplyQuote || currentMessageObject.messageOwner != null && currentMessageObject.messageOwner.reply_to != null && currentMessageObject.messageOwner.reply_to.reply_from != null)) { - delegate.didPressReplyMessage(ChatMessageCell.this, currentMessageObject.getReplyMsgId()); + delegate.didPressReplyMessage(ChatMessageCell.this, currentMessageObject.getReplyMsgId(), 0, 0, false); } } else if (virtualViewId == FORWARD) { if (delegate != null) { 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 1c2426e3491..576c5b04e92 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java @@ -1590,7 +1590,7 @@ public void buildLayout() { } w -= currentMessagePaint.measureText(": "); } - if (w > 0) { + if (w > 0 && message.messageTrimmedToHighlightCut) { text = AndroidUtilities.ellipsizeCenterEnd(text, message.highlightedWords.get(0), w, currentMessagePaint, 130).toString(); } messageString = new SpannableStringBuilder(emoji).append(text); @@ -1672,7 +1672,9 @@ public void buildLayout() { messageString = message.messageTrimmedToHighlight; } int w = getMeasuredWidth() - dp(messagePaddingStart + 23 ); - messageString = AndroidUtilities.ellipsizeCenterEnd(messageString, message.highlightedWords.get(0), w, currentMessagePaint, 130); + if (message.messageTrimmedToHighlightCut) { + messageString = AndroidUtilities.ellipsizeCenterEnd(messageString, message.highlightedWords.get(0), w, currentMessagePaint, 130); + } } else { SpannableStringBuilder stringBuilder = new SpannableStringBuilder(msgText); if (message != null) { @@ -1703,7 +1705,9 @@ public void buildLayout() { messageString = message.messageTrimmedToHighlight; } int w = getMeasuredWidth() - dp(messagePaddingStart + 23 + (thumbSize + 2) * thumbsCount - 2 + 5); - messageString = AndroidUtilities.ellipsizeCenterEnd(messageString, message.highlightedWords.get(0), w, currentMessagePaint, 130).toString(); + if (message.messageTrimmedToHighlightCut) { + messageString = AndroidUtilities.ellipsizeCenterEnd(messageString, message.highlightedWords.get(0), w, currentMessagePaint, 130).toString(); + } } else { if (messageString.length() > 150) { messageString = messageString.subSequence(0, 150); @@ -5174,7 +5178,7 @@ public SpannableStringBuilder getMessageStringFormatted(int messageFormatType, S } w -= currentMessagePaint.measureText(": "); } - if (w > 0) { + if (w > 0 && message.messageTrimmedToHighlightCut) { text = AndroidUtilities.ellipsizeCenterEnd(text, message.highlightedWords.get(0), w, currentMessagePaint, 130).toString(); } stringBuilder = new SpannableStringBuilder(emoji).append(text); 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 fa4e4ebebca..9bb9bbcbdf8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerActionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerActionCell.java @@ -22,11 +22,14 @@ import android.widget.TextView; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.DocumentObject; +import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.ImageLocation; import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; +import org.telegram.messenger.SvgHelper; import org.telegram.messenger.UserConfig; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; @@ -49,6 +52,7 @@ public DrawerActionCell(Context context) { imageView = new BackupImageView(context); imageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chats_menuItemIcon), PorterDuff.Mode.SRC_IN)); + imageView.getImageReceiver().setFileLoadingPriority(FileLoader.PRIORITY_HIGH); textView = new TextView(context); textView.setTextColor(Theme.getColor(Theme.key_chats_menuItemText)); @@ -157,7 +161,14 @@ public void setBot(TLRPC.TL_attachMenuBot bot) { } TLRPC.TL_attachMenuBotIcon botIcon = MediaDataController.getSideAttachMenuBotIcon(bot); if (botIcon != null) { - imageView.setImage(ImageLocation.getForDocument(botIcon.icon), "24_24", (Drawable) null, bot); + TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(botIcon.icon.thumbs, 24 * 3); + SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(botIcon.icon.thumbs, Theme.key_emptyListPlaceholder, 0.2f); + imageView.setImage( + ImageLocation.getForDocument(botIcon.icon), "24_24", + ImageLocation.getForDocument(photoSize, botIcon.icon), "24_24", + svgThumb != null ? svgThumb : getContext().getResources().getDrawable(R.drawable.msg_bot).mutate(), + bot + ); } else { imageView.setImageResource(R.drawable.msg_bot); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCreateUserCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCreateUserCell.java index 8b798d0f6a9..3829fff55ec 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCreateUserCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCreateUserCell.java @@ -30,6 +30,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.checkerframework.checker.units.qual.A; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ChatObject; import org.telegram.messenger.Emoji; @@ -64,6 +65,7 @@ public class GroupCreateUserCell extends FrameLayout { private CharSequence currentName; private CharSequence currentStatus; public boolean currentPremium; + public boolean currentMiniapps; private int checkBoxType; @@ -185,6 +187,7 @@ public void setObject(Object object, CharSequence name, CharSequence status) { currentName = name; drawDivider = false; currentPremium = false; + currentMiniapps = false; update(0); } @@ -198,6 +201,16 @@ public void setPremium() { statusTextView.setText(LocaleController.getString(R.string.PrivacyPremiumText)); } + public void setMiniapps() { + currentMiniapps = true; + currentObject = "miniapps"; + avatarImageView.setImageDrawable(makeMiniAppsDrawable(getContext(), false)); + nameTextView.setText(LocaleController.getString(R.string.PrivacyMiniapps)); + statusTextView.setTag(Theme.key_windowBackgroundWhiteGrayText); + statusTextView.setTextColor(Theme.getColor(forceDarkTheme ? Theme.key_voipgroup_lastSeenText : Theme.key_windowBackgroundWhiteGrayText, resourcesProvider)); + statusTextView.setText(LocaleController.getString(R.string.PrivacyMiniappsText)); + } + public static Drawable makePremiumUsersDrawable(Context context, boolean small) { PremiumGradient.PremiumGradientTools gradientTools = new PremiumGradient.PremiumGradientTools(Theme.key_premiumGradient2, Theme.key_premiumGradient1, -1, -1, -1, null); Drawable backgroundDrawable = new Drawable() { @@ -228,6 +241,14 @@ public int getOpacity() { return drawable; } + public static Drawable makeMiniAppsDrawable(Context context, boolean small) { + AvatarDrawable avatarDrawable = new AvatarDrawable(); + avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_FILTER_BOTS); + avatarDrawable.setScaleSize(small ? .8f : 1.1f); + avatarDrawable.setColor(Theme.getColor(Theme.key_avatar_backgroundBlue), Theme.getColor(Theme.key_avatar_background2Blue)); + return avatarDrawable; + } + public void setForbiddenCheck(boolean forbidden) { checkBox.setForbidden(forbidden); } @@ -299,7 +320,7 @@ public void setDrawDivider(boolean value) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(currentObject instanceof String && !"premium".equalsIgnoreCase((String) currentObject) ? 50 : 58), MeasureSpec.EXACTLY)); + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(currentObject instanceof String && !"premium".equalsIgnoreCase((String) currentObject) && !"miniapps".equalsIgnoreCase((String) currentObject) ? 50 : 58), MeasureSpec.EXACTLY)); } public void recycle() { @@ -307,7 +328,7 @@ public void recycle() { } public void update(int mask) { - if (currentObject == null || currentPremium) { + if (currentObject == null || currentPremium || currentMiniapps) { return; } TLRPC.FileLocation photo = null; 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 f4bca525666..3a7af2d30bb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java @@ -41,18 +41,21 @@ import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.AnimatedEmojiDrawable; import org.telegram.ui.Components.AnimatedFloat; import org.telegram.ui.Components.AvatarDrawable; +import org.telegram.ui.Components.ButtonBounce; import org.telegram.ui.Components.CanvasButton; import org.telegram.ui.Components.CheckBox2; import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.Premium.PremiumGradient; import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.Text; import org.telegram.ui.NotificationsSettingsActivity; import org.telegram.ui.Stories.StoriesUtilities; @@ -111,6 +114,7 @@ public class ProfileSearchCell extends BaseCell implements NotificationCenter.No private final AnimatedFloat premiumBlockedT = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); private boolean showPremiumBlocked; private boolean premiumBlocked; + private boolean openBot; private int statusLeft; private StaticLayout statusLayout; @@ -143,6 +147,14 @@ public ProfileSearchCell(Context context, Theme.ResourcesProvider resourcesProvi statusDrawable.setCallback(this); } + private boolean allowBotOpenButton; + private Utilities.Callback onOpenButtonClick; + public ProfileSearchCell allowBotOpenButton(boolean allow, Utilities.Callback onOpenClick) { + allowBotOpenButton = allow; + onOpenButtonClick = onOpenClick; + return this; + } + public ProfileSearchCell showPremiumBlock(boolean show) { showPremiumBlocked = show; return this; @@ -167,16 +179,21 @@ public void setData(Object object, TLRPC.EncryptedChat ec, CharSequence n, CharS chat = null; contact = null; premiumBlocked = showPremiumBlocked && user != null && MessagesController.getInstance(currentAccount).isUserPremiumBlocked(user.id); + setOpenBotButton(allowBotOpenButton && user.bot_has_main_app); } else if (object instanceof TLRPC.Chat) { chat = (TLRPC.Chat) object; user = null; contact = null; premiumBlocked = false; + setOpenBotButton(false); } else if (object instanceof ContactsController.Contact) { contact = (ContactsController.Contact) object; chat = null; user = null; premiumBlocked = showPremiumBlocked && contact != null && contact.user != null && MessagesController.getInstance(currentAccount).isUserPremiumBlocked(contact.user.id); + setOpenBotButton(false); + } else { + setOpenBotButton(false); } encryptedChat = ec; subLabel = s; @@ -185,6 +202,21 @@ public void setData(Object object, TLRPC.EncryptedChat ec, CharSequence n, CharS update(0); } + private final ButtonBounce openButtonBounce = new ButtonBounce(this); + private final Paint openButtonBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final RectF openButtonRect = new RectF(); + private Text openButtonText; + public void setOpenBotButton(boolean show) { + if (openBot == show) return; + if (openButtonText == null) { + openButtonText = new Text(LocaleController.getString(R.string.BotOpen), 14, AndroidUtilities.bold()); + } + int buttonWidth = show ? dp(14 + 14) + (int) openButtonText.getCurrentWidth() + dp(15 + 15) : 0; + setPadding(LocaleController.isRTL ? buttonWidth : 0, 0, LocaleController.isRTL ? 0 : buttonWidth, 0); + openBot = show; + openButtonBounce.setPressed(false); + } + public void setException(NotificationsSettingsActivity.NotificationException exception, CharSequence name) { String text; boolean enabled; @@ -492,7 +524,7 @@ public void buildLayout() { if (MessagesController.isSupportUser(user)) { statusString = LocaleController.getString(R.string.SupportStatus); } else if (user.bot && user.bot_active_users != 0) { - statusString = LocaleController.formatPluralStringSpaced("BotUsers", user.bot_active_users); + statusString = LocaleController.formatPluralStringSpaced("BotUsersShort", user.bot_active_users); } else if (user.bot) { statusString = LocaleController.getString(R.string.Bot); } else if (user.id == UserObject.VERIFY) { @@ -861,6 +893,21 @@ protected void onDraw(Canvas canvas) { lockDrawable.draw(canvas); canvas.restore(); } + + if (openBot && openButtonText != null) { + final float buttonWidth = dp(14 + 14) + openButtonText.getCurrentWidth(); + final float x = LocaleController.isRTL ? dp(15) : getWidth() - buttonWidth - dp(15); + final float h = dp(28); + + openButtonBackgroundPaint.setColor(Theme.getColor(Theme.key_featuredStickers_addButton)); + openButtonRect.set(x, (getHeight() - h) / 2.0f, x + buttonWidth, (getHeight() + h) / 2.0f); + canvas.save(); + final float s = openButtonBounce.getScale(.06f); + canvas.scale(s, s, openButtonRect.centerX(), openButtonRect.centerY()); + canvas.drawRoundRect(openButtonRect, openButtonRect.height() / 2.0f, openButtonRect.height() / 2.0f, openButtonBackgroundPaint); + openButtonText.draw(canvas, x + dp(14), getHeight() / 2.0f, 0xFFFFFFFF, 1.0f); + canvas.restore(); + } } public boolean isBlocked() { @@ -909,6 +956,23 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { @Override public boolean onTouchEvent(MotionEvent event) { + if (openBot && onOpenButtonClick != null && user != null) { + final boolean hit = openButtonRect.contains(event.getX(), event.getY()); + if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) { + openButtonBounce.setPressed(hit); + } else if (event.getAction() == MotionEvent.ACTION_UP) { + if (openButtonBounce.isPressed()) { + onOpenButtonClick.run(user); + } + openButtonBounce.setPressed(false); + return true; + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + openButtonBounce.setPressed(false); + return true; + } + if (hit || openButtonBounce.isPressed()) + return true; + } if ((user != null || chat != null) && avatarStoryParams.checkOnTouchEvent(event, this)) { return true; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java index 500b599cb9c..1142af15f8a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java @@ -8,6 +8,8 @@ package org.telegram.ui.Cells; +import static org.telegram.messenger.AndroidUtilities.dp; + import android.content.Context; import android.graphics.Canvas; import android.graphics.ColorFilter; @@ -80,18 +82,18 @@ public SessionCell(Context context, int type) { addView(linearLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 30, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, (LocaleController.isRTL ? 15 : 49), 11, (LocaleController.isRTL ? 49 : 15), 0)); avatarDrawable = new AvatarDrawable(); - avatarDrawable.setTextSize(AndroidUtilities.dp(10)); + avatarDrawable.setTextSize(dp(10)); imageView = new BackupImageView(context); - imageView.setRoundRadius(AndroidUtilities.dp(10)); + imageView.setRoundRadius(dp(10)); addView(imageView, LayoutHelper.createFrame(20, 20, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, (LocaleController.isRTL ? 0 : 21), 13, (LocaleController.isRTL ? 21 : 0), 0)); } else { placeholderImageView = new BackupImageView(context); - placeholderImageView.setRoundRadius(AndroidUtilities.dp(10)); + placeholderImageView.setRoundRadius(dp(10)); addView(placeholderImageView, LayoutHelper.createFrame(42, 42, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, (LocaleController.isRTL ? 0 : 16), 9, (LocaleController.isRTL ? 16 : 0), 0)); imageView = new BackupImageView(context); - imageView.setRoundRadius(AndroidUtilities.dp(10)); + imageView.setRoundRadius(dp(10)); addView(imageView, LayoutHelper.createFrame(42, 42, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, (LocaleController.isRTL ? 0 : 16), 9, (LocaleController.isRTL ? 16 : 0), 0)); addView(linearLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 30, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, (LocaleController.isRTL ? 15 : 72), 6.333f, (LocaleController.isRTL ? 72 : 15), 0)); @@ -177,7 +179,7 @@ private void setContentAlpha(float alpha) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(currentType == 0 ? 70 : 90) + (needDivider ? 1 : 0), MeasureSpec.EXACTLY)); + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(dp(currentType == 0 ? 70 : 90) + (needDivider ? 1 : 0), MeasureSpec.EXACTLY)); } public void setSession(TLObject object, boolean divider) { @@ -219,7 +221,7 @@ public void setSession(TLObject object, boolean divider) { } if (spannableStringBuilder.length() != 0) { DotDividerSpan dotDividerSpan = new DotDividerSpan(); - dotDividerSpan.setTopPadding(AndroidUtilities.dp(1.5f)); + dotDividerSpan.setTopPadding(dp(1.5f)); spannableStringBuilder.append(" . ").setSpan(dotDividerSpan, spannableStringBuilder.length() - 2, spannableStringBuilder.length() - 1, 0); } spannableStringBuilder.append(timeText); @@ -358,6 +360,10 @@ public static CombinedDrawable createDrawable(int sz, TLRPC.TL_authorization ses iconId = R.drawable.msg_channel; colorKey = Theme.key_avatar_backgroundPink; colorKey2 = Theme.key_avatar_background2Pink; + } else if (platform.contains("api")) { + iconId = R.drawable.filled_paid_broadcast; + colorKey = Theme.key_avatar_backgroundGreen; + colorKey2 = Theme.key_avatar_background2Green; } else if (platform.equals("?")) { iconId = R.drawable.msg_emoji_question; colorKey = -1; @@ -373,9 +379,8 @@ public static CombinedDrawable createDrawable(int sz, TLRPC.TL_authorization ses } Drawable iconDrawable = ContextCompat.getDrawable(ApplicationLoader.applicationContext, iconId).mutate(); iconDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_avatar_text), PorterDuff.Mode.SRC_IN)); - Drawable bgDrawable = new CircleGradientDrawable(AndroidUtilities.dp(sz), colorKey == -1 ? 0xFF000000 : Theme.getColor(colorKey), colorKey2 == -1 ? 0xFF000000 : Theme.getColor(colorKey2)); - CombinedDrawable combinedDrawable = new CombinedDrawable(bgDrawable, iconDrawable); - return combinedDrawable; + Drawable bgDrawable = new CircleGradientDrawable(dp(sz), colorKey == -1 ? 0xFF000000 : Theme.getColor(colorKey), colorKey2 == -1 ? 0xFF000000 : Theme.getColor(colorKey2)); + return new CombinedDrawable(bgDrawable, iconDrawable); } public static class CircleGradientDrawable extends Drawable { @@ -434,23 +439,23 @@ protected void onDraw(Canvas canvas) { View parent = (View) getParent(); globalGradient.setParentSize(parent.getMeasuredWidth(), parent.getMeasuredHeight(), -getX()); } - float y = linearLayout.getTop() + nameTextView.getTop() + AndroidUtilities.dp(12); + float y = linearLayout.getTop() + nameTextView.getTop() + dp(12); float x = linearLayout.getX(); - AndroidUtilities.rectTmp.set(x, y - AndroidUtilities.dp(4), x + getMeasuredWidth() * 0.2f, y + AndroidUtilities.dp(4)); - canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(4), AndroidUtilities.dp(4), globalGradient.getPaint()); + AndroidUtilities.rectTmp.set(x, y - dp(4), x + getMeasuredWidth() * 0.2f, y + dp(4)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(4), dp(4), globalGradient.getPaint()); - y = linearLayout.getTop() + detailTextView.getTop() - AndroidUtilities.dp(1); + y = linearLayout.getTop() + detailTextView.getTop() - dp(1); x = linearLayout.getX(); - AndroidUtilities.rectTmp.set(x, y - AndroidUtilities.dp(4), x + getMeasuredWidth() * 0.4f, y + AndroidUtilities.dp(4)); - canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(4), AndroidUtilities.dp(4), globalGradient.getPaint()); + AndroidUtilities.rectTmp.set(x, y - dp(4), x + getMeasuredWidth() * 0.4f, y + dp(4)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(4), dp(4), globalGradient.getPaint()); - y = linearLayout.getTop() + detailExTextView.getTop() - AndroidUtilities.dp(1); + y = linearLayout.getTop() + detailExTextView.getTop() - dp(1); x = linearLayout.getX(); - AndroidUtilities.rectTmp.set(x, y - AndroidUtilities.dp(4), x + getMeasuredWidth() * 0.3f, y + AndroidUtilities.dp(4)); - canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(4), AndroidUtilities.dp(4), globalGradient.getPaint()); + AndroidUtilities.rectTmp.set(x, y - dp(4), x + getMeasuredWidth() * 0.3f, y + dp(4)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(4), dp(4), globalGradient.getPaint()); invalidate(); if (stubAlpha < 1f) { @@ -459,7 +464,7 @@ protected void onDraw(Canvas canvas) { } if (needDivider) { int margin = currentType == 1 ? 49 : 72; - canvas.drawLine(LocaleController.isRTL ? 0 : AndroidUtilities.dp(margin), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? AndroidUtilities.dp(margin) : 0), getMeasuredHeight() - 1, Theme.dividerPaint); + canvas.drawLine(LocaleController.isRTL ? 0 : dp(margin), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? dp(margin) : 0), getMeasuredHeight() - 1, Theme.dividerPaint); } } @@ -469,7 +474,7 @@ public void showStub(FlickerLoadingView globalGradient) { Drawable iconDrawable = ContextCompat.getDrawable(ApplicationLoader.applicationContext, AndroidUtilities.isTablet() ? R.drawable.device_tablet_android : R.drawable.device_phone_android).mutate(); iconDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_avatar_text), PorterDuff.Mode.SRC_IN)); - CombinedDrawable combinedDrawable = new CombinedDrawable(Theme.createCircleDrawable(AndroidUtilities.dp(42), Theme.getColor(Theme.key_avatar_backgroundGreen)), iconDrawable); + CombinedDrawable combinedDrawable = new CombinedDrawable(Theme.createCircleDrawable(dp(42), Theme.getColor(Theme.key_avatar_backgroundGreen)), iconDrawable); if (placeholderImageView != null) { placeholderImageView.setImageDrawable(combinedDrawable); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCell.java index c7cd6b0a1ef..1049d0340f9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCell.java @@ -458,7 +458,7 @@ public void setTextAndValue(CharSequence text, CharSequence value, boolean anima } } - public void setValue(String value, boolean animated) { + public void setValue(CharSequence value, boolean animated) { valueTextView.setText(value == null ? "" : TextUtils.ellipsize(valueText = value, valueTextView.getPaint(), AndroidUtilities.displaySize.x / 2.5f, TextUtils.TruncateAt.END), animated); } @@ -482,6 +482,31 @@ public void setTextAndValueAndColorfulIcon(String text, CharSequence value, bool } } + public void setTextAndCheckAndColorfulIcon(CharSequence text, boolean checked, int resId, int color, boolean divider) { + imageLeft = 21; + offsetFromImage = getOffsetFromImage(false); + textView.setText(text); + textView.setRightDrawable(null); + valueTextView.setVisibility(GONE); + valueSpoilersTextView.setVisibility(GONE); + valueImageView.setVisibility(GONE); + setColorfulIcon(color, resId); + if (checkBox == null) { + checkBox = new Switch(getContext(), resourcesProvider); + checkBox.setColors(Theme.key_switchTrack, Theme.key_switchTrackChecked, Theme.key_windowBackgroundWhite, Theme.key_windowBackgroundWhite); + addView(checkBox, LayoutHelper.createFrame(37, 20, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL, 22, 0, 22, 0)); + } + if (checkBox != null) { + checkBox.setVisibility(VISIBLE); + checkBox.setChecked(checked, false); + } + needDivider = divider; + setWillNotDraw(!needDivider); + if (emojiDrawable != null) { + emojiDrawable.set((Drawable) null, false); + } + } + public void setTextAndSpoilersValueAndIcon(String text, CharSequence value, int resId, boolean divider) { imageLeft = 21; offsetFromImage = getOffsetFromImage(false); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java index b918d55fca5..38b08d344dc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java @@ -26,6 +26,7 @@ import android.widget.TextView; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.BuildVars; import org.telegram.messenger.LocaleController; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.AnimatedEmojiDrawable; @@ -111,12 +112,18 @@ public ImageView getValueImageView() { return valueImageView; } + private boolean betterLayout = BuildVars.DEBUG_PRIVATE_VERSION; + public void setBetterLayout(boolean betterLayout) { + // I might break something with this, gonna need to further test + this.betterLayout = betterLayout; + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), AndroidUtilities.dp(50) + (needDivider ? 1 : 0)); int availableWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - AndroidUtilities.dp(34); - int width = availableWidth / 2; + int width = betterLayout ? availableWidth : availableWidth / 2; if (valueImageView.getVisibility() == VISIBLE) { valueImageView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); } @@ -127,14 +134,20 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } else { imageView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST)); } + if (betterLayout) width -= imageView.getMeasuredWidth() + AndroidUtilities.dp(8); } if (valueBackupImageView != null) { valueBackupImageView.measure(MeasureSpec.makeMeasureSpec(valueBackupImageView.getLayoutParams().height, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(valueBackupImageView.getLayoutParams().width, MeasureSpec.EXACTLY)); + if (betterLayout) width -= valueBackupImageView.getMeasuredWidth() + AndroidUtilities.dp(8); } if (valueTextView.getVisibility() == VISIBLE) { valueTextView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); - width = availableWidth - valueTextView.getMeasuredWidth() - AndroidUtilities.dp(8); + if (betterLayout) { + width -= valueTextView.getMeasuredWidth() + AndroidUtilities.dp(8); + } else { + width = availableWidth - valueTextView.getMeasuredWidth() - AndroidUtilities.dp(8); + } if (valueImageView.getVisibility() == VISIBLE) { MarginLayoutParams params = (MarginLayoutParams) valueImageView.getLayoutParams(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ThemePreviewMessagesCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ThemePreviewMessagesCell.java index 879d628cd83..7cacfc948fc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ThemePreviewMessagesCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ThemePreviewMessagesCell.java @@ -390,7 +390,7 @@ public boolean canPerformActions() { } @Override - public void didPressReplyMessage(ChatMessageCell cell, int id) { + public void didPressReplyMessage(ChatMessageCell cell, int id, float x, float y, boolean longpress) { if (allowLoadingOnTouch()) { progress = ChatActivity.PROGRESS_REPLY; cell.invalidate(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java index 24975b3c1da..b472cb56fc1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java @@ -1666,7 +1666,7 @@ public void updateDrawState(@NonNull TextPaint ds) { stickerSet = action.prev_stickerset; } if (stickerSet != null) { - showDialog(new StickersAlert(getParentActivity(), ChannelAdminLogActivity.this, stickerSet, null, null)); + showDialog(new StickersAlert(getParentActivity(), ChannelAdminLogActivity.this, stickerSet, null, null, false)); return true; } } else if (selectedObject.currentEvent != null && selectedObject.currentEvent.action instanceof TLRPC.TL_channelAdminLogEventActionChangeEmojiStickerSet) { @@ -2209,7 +2209,7 @@ private void processSelectedOption(int option) { break; } case OPTION_SAVE_STICKER: { - showDialog(new StickersAlert(getParentActivity(), this, selectedObject.getInputStickerSet(), null, null)); + showDialog(new StickersAlert(getParentActivity(), this, selectedObject.getInputStickerSet(), null, null, false)); break; } case OPTION_SAVE_TO_DOWNLOADS_OR_MUSIC: { @@ -3081,7 +3081,7 @@ public void needOpenWebView(MessageObject message, String url, String title, Str } @Override - public void didPressReplyMessage(ChatMessageCell cell, int id) { + public void didPressReplyMessage(ChatMessageCell cell, int id, float x, float y, boolean longpress) { MessageObject messageObject = cell.getMessageObject(); MessageObject reply = messageObject.replyMessageObject; if (reply.getDialogId() == -currentChat.id) { @@ -3108,7 +3108,7 @@ public void didPressViaBot(ChatMessageCell cell, String username) { public void didPressImage(ChatMessageCell cell, float x, float y) { MessageObject message = cell.getMessageObject(); if (message.getInputStickerSet() != null) { - showDialog(new StickersAlert(getParentActivity(), ChannelAdminLogActivity.this, message.getInputStickerSet(), null, null)); + showDialog(new StickersAlert(getParentActivity(), ChannelAdminLogActivity.this, message.getInputStickerSet(), null, null, false)); } else if (message.isVideo() || message.type == MessageObject.TYPE_PHOTO || message.type == MessageObject.TYPE_TEXT && !message.isWebpageDocument() || message.isGif()) { PhotoViewer.getInstance().setParentActivity(ChannelAdminLogActivity.this); PhotoViewer.getInstance().openPhoto(message, null, 0, 0, 0, provider); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChannelMonetizationLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/ChannelMonetizationLayout.java index b2a35466d07..4f3e8dbbea1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChannelMonetizationLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChannelMonetizationLayout.java @@ -187,7 +187,7 @@ public ChannelMonetizationLayout( initLevel(); titleInfo = AndroidUtilities.replaceArrows(AndroidUtilities.replaceSingleTag(formatString(R.string.MonetizationInfo, 50), -1, REPLACING_TAG_TYPE_LINK_NBSP, () -> { - showLearnSheet(); + fragment.showDialog(makeLearnSheet(context, false, resourcesProvider)); }, resourcesProvider), true); balanceInfo = AndroidUtilities.replaceArrows(AndroidUtilities.replaceSingleTag(getString(MessagesController.getInstance(currentAccount).channelRevenueWithdrawalEnabled ? R.string.MonetizationBalanceInfo : R.string.MonetizationBalanceInfoNotAvailable), -1, REPLACING_TAG_TYPE_LINK_NBSP, () -> { Browser.openUrl(getContext(), getString(R.string.MonetizationBalanceInfoLink)); @@ -525,7 +525,7 @@ private void initWithdraw(boolean stars, TLRPC.InputCheckPasswordSRP password, T r = req; } else { TL_stats.TL_getBroadcastRevenueWithdrawalUrl req = new TL_stats.TL_getBroadcastRevenueWithdrawalUrl(); - req.channel = MessagesController.getInstance(currentAccount).getInputChannel(-dialogId); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(dialogId); req.password = password != null ? password : new TLRPC.TL_inputCheckPasswordEmpty(); r = req; } @@ -701,7 +701,7 @@ private void setStarsBalance(long crypto_amount, int blockedUntil) { private void loadStarsStats() { if (!starsRevenueAvailable) return; - TLRPC.TL_payments_starsRevenueStats cachedStats = BotStarsController.getInstance(currentAccount).getRevenueStats(dialogId); + TLRPC.TL_payments_starsRevenueStats cachedStats = BotStarsController.getInstance(currentAccount).getStarsRevenueStats(dialogId); if (cachedStats != null) { AndroidUtilities.runOnUIThread(() -> { applyStarsStats(cachedStats); @@ -763,7 +763,7 @@ private void initLevel() { } else { TL_stats.TL_getBroadcastRevenueStats getBroadcastStats = new TL_stats.TL_getBroadcastRevenueStats(); getBroadcastStats.dark = Theme.isCurrentThemeDark(); - getBroadcastStats.channel = MessagesController.getInstance(currentAccount).getInputChannel(-dialogId); + getBroadcastStats.peer = MessagesController.getInstance(currentAccount).getInputPeer(dialogId); req = getBroadcastStats; } int stats_dc = -1; @@ -880,7 +880,7 @@ public void setActionBar(ActionBar actionBar) { private void checkLearnSheet() { if (isAttachedToWindow() && tonRevenueAvailable && proceedsAvailable && MessagesController.getGlobalMainSettings().getBoolean("monetizationadshint", true)) { - showLearnSheet(); + fragment.showDialog(makeLearnSheet(getContext(), false, resourcesProvider)); MessagesController.getGlobalMainSettings().edit().putBoolean("monetizationadshint", false).apply(); } } @@ -1344,7 +1344,7 @@ private static byte[] crc16(byte[] data) { return result; } - private static void showTransactionSheet(Context context, int currentAccount, TL_stats.BroadcastRevenueTransaction transaction, long dialogId, Theme.ResourcesProvider resourcesProvider) { + public static void showTransactionSheet(Context context, int currentAccount, TL_stats.BroadcastRevenueTransaction transaction, long dialogId, Theme.ResourcesProvider resourcesProvider) { BottomSheet sheet = new BottomSheet(context, false, resourcesProvider); sheet.fixNavigationBar(); @@ -1502,49 +1502,49 @@ private static void showTransactionSheet(Context context, int currentAccount, TL sheet.show(); } - private void showLearnSheet() { - BottomSheet sheet = new BottomSheet(getContext(), false, resourcesProvider); + public static BottomSheet makeLearnSheet(Context context, boolean bots, Theme.ResourcesProvider resourcesProvider) { + BottomSheet sheet = new BottomSheet(context, false, resourcesProvider); sheet.fixNavigationBar(); - LinearLayout layout = new LinearLayout(getContext()); + LinearLayout layout = new LinearLayout(context); layout.setOrientation(LinearLayout.VERTICAL); layout.setPadding(dp(8), 0, dp(8), 0); - RLottieImageView imageView = new RLottieImageView(getContext()); + RLottieImageView imageView = new RLottieImageView(context); imageView.setScaleType(ImageView.ScaleType.CENTER); imageView.setImageResource(R.drawable.large_monetize); imageView.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); imageView.setBackground(Theme.createCircleDrawable(dp(80), Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider))); layout.addView(imageView, LayoutHelper.createLinear(80, 80, Gravity.CENTER_HORIZONTAL, 0, 16, 0, 16)); - TextView textView = new TextView(getContext()); + TextView textView = new TextView(context); textView.setGravity(Gravity.CENTER); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); textView.setTypeface(AndroidUtilities.bold()); textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); - textView.setText(getString(R.string.MonetizationInfoTitle)); + textView.setText(getString(bots ? R.string.BotMonetizationInfoTitle : R.string.MonetizationInfoTitle)); layout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 8, 0, 8, 25)); layout.addView( - new FeatureCell(getContext(), R.drawable.msg_channel, getString(R.string.MonetizationInfoFeature1Name), getString(R.string.MonetizationInfoFeature1Text)), + new FeatureCell(context, R.drawable.msg_channel, getString(bots ? R.string.BotMonetizationInfoFeature1Name : R.string.MonetizationInfoFeature1Name), getString(bots ? R.string.BotMonetizationInfoFeature1Text : R.string.MonetizationInfoFeature1Text), resourcesProvider), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 0, 0, 16) ); layout.addView( - new FeatureCell(getContext(), R.drawable.menu_feature_split, getString(R.string.MonetizationInfoFeature2Name), getString(R.string.MonetizationInfoFeature2Text)), + new FeatureCell(context, R.drawable.menu_feature_split, getString(bots ? R.string.BotMonetizationInfoFeature2Name : R.string.MonetizationInfoFeature2Name), getString(bots ? R.string.BotMonetizationInfoFeature2Text : R.string.MonetizationInfoFeature2Text), resourcesProvider), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 0, 0, 16) ); layout.addView( - new FeatureCell(getContext(), R.drawable.menu_feature_withdrawals, getString(R.string.MonetizationInfoFeature3Name), getString(R.string.MonetizationInfoFeature3Text)), + new FeatureCell(context, R.drawable.menu_feature_withdrawals, getString(bots ? R.string.BotMonetizationInfoFeature3Name : R.string.MonetizationInfoFeature3Name), getString(bots ? R.string.BotMonetizationInfoFeature3Text : R.string.MonetizationInfoFeature3Text), resourcesProvider), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 0, 0, 16) ); - View separator = new View(getContext()); + View separator = new View(context); separator.setBackgroundColor(Theme.getColor(Theme.key_divider, resourcesProvider)); layout.addView(separator, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 1f / AndroidUtilities.density, Gravity.TOP | Gravity.FILL_HORIZONTAL, 12, 0, 12, 0)); - textView = new AnimatedEmojiSpan.TextViewEmojis(getContext()); + textView = new AnimatedEmojiSpan.TextViewEmojis(context); textView.setGravity(Gravity.CENTER); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); textView.setTypeface(AndroidUtilities.bold()); @@ -1556,18 +1556,18 @@ private void showLearnSheet() { span.setRelativeSize(textView.getPaint().getFontMetricsInt()); span.spaceScaleX = .9f; animatedDiamond.setSpan(span, 0, animatedDiamond.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - textView.setText(AndroidUtilities.replaceCharSequence("💎", getString(R.string.MonetizationInfoTONTitle), animatedDiamond)); + textView.setText(AndroidUtilities.replaceCharSequence("💎", getString(bots ? R.string.BotMonetizationInfoTONTitle : R.string.MonetizationInfoTONTitle), animatedDiamond)); layout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 8, 20, 8, 0)); - textView = new LinkSpanDrawable.LinksTextView(getContext(), resourcesProvider); + textView = new LinkSpanDrawable.LinksTextView(context, resourcesProvider); textView.setGravity(Gravity.CENTER); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); textView.setLinkTextColor(Theme.getColor(Theme.key_chat_messageLinkIn, resourcesProvider)); - textView.setText(AndroidUtilities.withLearnMore(AndroidUtilities.replaceTags(getString(R.string.MonetizationInfoTONText)), () -> Browser.openUrl(getContext(), getString(R.string.MonetizationInfoTONLink)))); + textView.setText(AndroidUtilities.withLearnMore(AndroidUtilities.replaceTags(getString(bots ? R.string.BotMonetizationInfoTONText : R.string.MonetizationInfoTONText)), () -> Browser.openUrl(context, getString(bots ? R.string.BotMonetizationInfoTONLink : R.string.MonetizationInfoTONLink)))); layout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 28, 9, 28, 0)); - ButtonWithCounterView button = new ButtonWithCounterView(getContext(), resourcesProvider); + ButtonWithCounterView button = new ButtonWithCounterView(context, resourcesProvider); button.setText(getString(R.string.GotIt), false); button.setOnClickListener(v -> { sheet.dismiss(); @@ -1576,11 +1576,11 @@ private void showLearnSheet() { sheet.setCustomView(layout); - fragment.showDialog(sheet); + return sheet; } - private class FeatureCell extends FrameLayout { - public FeatureCell(Context context, int icon, CharSequence header, CharSequence text) { + private static class FeatureCell extends FrameLayout { + public FeatureCell(Context context, int icon, CharSequence header, CharSequence text, Theme.ResourcesProvider resourcesProvider) { super(context); ImageView imageView = new ImageView(context); @@ -1779,7 +1779,7 @@ private void loadTransactions(int type) { return; loadingTransactions[type] = true; TL_stats.TL_getBroadcastRevenueTransactions req = new TL_stats.TL_getBroadcastRevenueTransactions(); - req.channel = MessagesController.getInstance(currentAccount).getInputChannel(-dialogId); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(dialogId); req.offset = tonTransactions.size(); req.limit = tonTransactions.isEmpty() ? 5 : 20; ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java index 9584a7486f9..9565beb91e3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java @@ -76,7 +76,6 @@ 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.Property; import android.util.SparseArray; @@ -249,10 +248,12 @@ import org.telegram.ui.Stars.StarsReactionsSheet; import org.telegram.ui.Stories.StoriesListPlaceProvider; import org.telegram.ui.Stories.StoriesUtilities; +import org.telegram.ui.Stories.PublicStoriesList; import org.telegram.ui.Stories.recorder.HintView2; import org.telegram.ui.Stories.recorder.PreviewView; import org.telegram.ui.Stories.recorder.StoryEntry; import org.telegram.ui.Stories.recorder.StoryRecorder; +import org.telegram.ui.bots.BotAdView; import org.telegram.ui.bots.BotCommandsMenuView; import org.telegram.ui.bots.BotWebViewAttachedSheet; import org.telegram.ui.bots.BotWebViewSheet; @@ -265,7 +266,6 @@ import java.io.FileWriter; import java.io.InputStream; import java.io.InputStreamReader; -import java.net.IDN; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; @@ -403,6 +403,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private int defaultSearchPage; private boolean requestClearSearchPages; private HashtagHistoryView hashtagHistoryView; + private AlertDialog scheduleNowDialog; private HintView2 savedMessagesHint; private HintView2 savedMessagesSearchHint; @@ -410,6 +411,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private HintView2 groupEmojiPackHint; private HintView2 botMessageHint; private HintView2 factCheckHint; + private HintView2 videoConversionTimeHint; + private float videoConversionTimeHintY; private int reactionsMentionCount; private FrameLayout reactionsMentiondownButton; @@ -432,7 +435,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not public ChatAttachAlert chatAttachAlert; @Nullable private BlurredFrameLayout topChatPanelView; + @Nullable + private BlurredFrameLayout topChatPanelView2; private AnimatorSet reportSpamViewAnimator; + private AnimatorSet topChatPanelView2Animator; @Nullable private TextView addToContactsButton; private boolean addToContactsButtonArchive; @@ -450,6 +456,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private LinkSpanDrawable.LinksTextView emojiStatusSpamHint; @Nullable private ImageView closeReportSpam; + private BotAdView botAdView; private TextView chatWithAdminTextView; private FragmentContextView fragmentContextView; private FragmentContextView fragmentLocationContextView; @@ -557,7 +564,7 @@ public ArrayList getFilteredMessages() { private MessageObject hint2MessageObject; private FrameLayout messagesSearchListContainer; - private RecyclerListView messagesSearchListView; + public RecyclerListView messagesSearchListView; private MessagesSearchAdapter messagesSearchAdapter; private AnimatorSet messagesSearchListViewAnimation; @@ -572,6 +579,7 @@ public ArrayList getFilteredMessages() { public static final int SEARCH_THIS_CHAT = 0; public static final int SEARCH_MY_MESSAGES = 1; public static final int SEARCH_PUBLIC_POSTS = 2; + public static final int SEARCH_CHANNEL_POSTS = 3; private int searchType; public TLRPC.TL_businessChatLink businessLink = null; @@ -607,6 +615,8 @@ public boolean isReport() { private SparseArray pendingSendMessagesDict = new SparseArray<>(); private ArrayList pendingSendMessages = new ArrayList<>(); private int threadUnreadMessagesCount; + private boolean convertingToast, convertingToastShown; + private int convertingToastMessageId; public ArrayList animatingMessageObjects = new ArrayList<>(); private HashMap animatingDocuments = new HashMap<>(); @@ -650,6 +660,7 @@ public boolean isReport() { private AnimatorSet forwardButtonAnimation; SparseIntArray dateObjectsStableIds = new SparseIntArray(); + SparseIntArray conversionObjectsStableIds = new SparseIntArray(); public static int lastStableId = 10; private boolean openSearchKeyboard; @@ -744,6 +755,7 @@ public boolean isReport() { private SparseArray> replyMessageOwners = new SparseArray<>(); private HashMap> messagesByDays = new HashMap<>(); private SparseArray> messagesByDaysSorted = new SparseArray<>(); + private LongSparseArray conversionMessages = new LongSparseArray<>(); public ArrayList messages = new ArrayList<>(); private SparseArray waitingForReplies = new SparseArray<>(); private LongSparseArray> polls = new LongSparseArray<>(); @@ -889,6 +901,7 @@ public int getColor(int key) { private Paint scrimPaint; private Paint actionBarBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private float scrimPaintAlpha = 0f; + private boolean scrimProgressDirection; private View scrimView; private float scrimViewAlpha = 1f; private float scrimViewProgress = 0f; @@ -915,7 +928,7 @@ public void run() { } else { ramainingStr = LocaleController.formatPluralString("Days", Math.round(remaining / (24 * 60 * 60.0f))); } - menuDeleteItem.setSubtext(LocaleController.formatString("AutoDeleteIn", R.string.AutoDeleteIn, ramainingStr)); + menuDeleteItem.setSubtext(LocaleController.formatString(R.string.AutoDeleteIn, ramainingStr)); AndroidUtilities.runOnUIThread(updateDeleteItemRunnable, 1000); } }; @@ -963,6 +976,7 @@ public void run() { private float contentPanTranslationT; private float floatingDateViewOffset; private float topChatPanelViewOffset; + private float topChatPanelView2Offset; private float pinnedMessageEnterOffset; private float topViewOffset; private TLRPC.Document preloadedGreetingsSticker; @@ -2113,7 +2127,7 @@ public void onStickersTab(boolean opened) { @Override public void didPressAttachButton() { if (chatAttachAlert != null) { - chatAttachAlert.setEditingMessageObject(null); + chatAttachAlert.setEditingMessageObject(0, null); } openAttachMenu(); } @@ -2353,6 +2367,8 @@ public boolean onFragmentCreate() { startLoadFromDate = arguments.getInt("start_from_date", 0); startFromVideoTimestamp = arguments.getInt("video_timestamp", -1); threadUnreadMessagesCount = arguments.getInt("unread_count", 0); + convertingToast = arguments.getBoolean("converting_toast", false); + convertingToastMessageId = arguments.getInt("converting_toast_from", 0); if (startFromVideoTimestamp >= 0) { startFromVideoMessageId = startLoadFromMessageId; } @@ -2818,6 +2834,10 @@ protected void updateSearchingHashtag(String hashtag) { firstMessagesLoaded = false; HashtagSearchController.getInstance(currentAccount).clearSearchResults(searchType); messagesSearchAdapter.notifyDataSetChanged(); + messagesSearchListView.requestLayout(); + if (messagesSearchListView.getLayoutManager() != null) { + messagesSearchListView.getLayoutManager().scrollToPosition(0); + } updateSearchListEmptyView(); hashtagSearchEmptyView.showProgress(true); firstLoadMessages(); @@ -3535,7 +3555,7 @@ public void run(boolean revoke) { attach.setOnClickListener(view -> { headerItem.closeSubMenu(); if (chatAttachAlert != null) { - chatAttachAlert.setEditingMessageObject(null); + chatAttachAlert.setEditingMessageObject(0, null); } openAttachMenu(); }); @@ -4042,6 +4062,11 @@ public void toggleMute() { scrimPaint = new Paint(); + if (chatListThanosEffect != null) { + AndroidUtilities.removeFromParent(chatListThanosEffect); + chatListThanosEffect = null; + } + removingFromParent = false; fragmentView = contentView = new ChatActivityFragmentView(context, parentLayout); contentView.needBlur = true; contentView.needBlurBottom = true; @@ -4659,7 +4684,7 @@ public void onDraw(Canvas c) { drawReplyButton(c); } - if (pullingDownOffset != 0 && !isInPreviewMode() && !isInsideContainer && chatMode != MODE_SAVED) { + if (pullingDownOffset != 0 && !isInPreviewMode() && !isInsideContainer && chatMode != MODE_SAVED && chatMode != MODE_SCHEDULED) { c.save(); float transitionOffset = 0; if (pullingDownAnimateProgress != 0) { @@ -5885,36 +5910,9 @@ public void scrollToPositionWithOffset(int position, int offset, boolean bottom) if (!bottom) { offset = (int) (offset - getPaddingTop() + chatListViewPaddingTop); } - if (BuildVars.DEBUG_PRIVATE_VERSION) { - FileLog.e("scrollToPositionWithOffset " + position + " " + offset + " " + bottom, new Exception()); - } super.scrollToPositionWithOffset(position, offset, bottom); } - @Override - public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { - if (BuildVars.DEBUG_PRIVATE_VERSION) { - FileLog.e("scrollHorizontallyBy " + dx, new Exception()); - } - return super.scrollHorizontallyBy(dx, recycler, state); - } - - @Override - public void scrollToPosition(int position) { - if (BuildVars.DEBUG_PRIVATE_VERSION) { - FileLog.e("scrollToPosition " + position, new Exception()); - } - super.scrollToPosition(position); - } - - @Override - public void scrollToPositionWithOffset(int position, int offset) { - if (BuildVars.DEBUG_PRIVATE_VERSION) { - FileLog.e("scrollToPositionWithOffset " + position + " " + offset, new Exception()); - } - super.scrollToPositionWithOffset(position, offset); - } - @Override public boolean supportsPredictiveItemAnimations() { return true; @@ -5923,9 +5921,6 @@ public boolean supportsPredictiveItemAnimations() { @Override public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) { scrollByTouch = false; - if (BuildVars.DEBUG_PRIVATE_VERSION) { - FileLog.e("smoothScrollToPosition " + position, new Exception()); - } LinearSmoothScrollerCustom linearSmoothScroller = new LinearSmoothScrollerCustom(recyclerView.getContext(), LinearSmoothScrollerCustom.POSITION_MIDDLE); linearSmoothScroller.setTargetPosition(position); startSmoothScroll(linearSmoothScroller); @@ -6014,7 +6009,7 @@ public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerVi if (!foundTopView) { scrolled = super.scrollVerticallyBy(dy, recycler, state); } - if (dy > 0 && scrolled == 0 && (ChatObject.isChannel(currentChat) && !currentChat.megagroup || isTopic) && chatMode != MODE_SAVED && chatListView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING && !chatListView.isFastScrollAnimationRunning() && !chatListView.isMultiselect() && !isReport()) { + if (dy > 0 && scrolled == 0 && (ChatObject.isChannel(currentChat) && !currentChat.megagroup || isTopic) && chatMode != MODE_SAVED && chatMode != MODE_SCHEDULED && chatListView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING && !chatListView.isFastScrollAnimationRunning() && !chatListView.isMultiselect() && !isReport()) { if (pullingDownOffset == 0 && pullingDownDrawable != null) { if (nextChannels != null && !nextChannels.isEmpty()) { pullingDownDrawable.updateDialog(nextChannels.get(0)); @@ -6265,6 +6260,9 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { AndroidUtilities.runOnUIThread(ChatActivity.this::checkSavedMessagesTagHint, 2000); } } + if (videoConversionTimeHint != null && videoConversionTimeHint.shown()) { + videoConversionTimeHint.hide(); + } if (botMessageHint != null && botMessageHint.shown()) { botMessageHint.hide(); } else { @@ -6406,6 +6404,7 @@ protected void onDraw(Canvas canvas) { undoView = null; topUndoView = null; topChatPanelView = null; + topChatPanelView2 = null; reportSpamButton = null; emojiStatusSpamHint = null; topViewSeparator1 = null; @@ -6415,6 +6414,7 @@ protected void onDraw(Canvas canvas) { restartTopicButton = null; closeReportSpam = null; translateButton = null; + botAdView = null; bizBotButton = null; pagedownButton = new FrameLayout(context); @@ -6672,6 +6672,7 @@ public boolean dispatchTouchEvent(MotionEvent ev) { if (isInPreviewMode()) { mentionContainer.setAlpha(0f); } + mentionContainer.setDialogId(dialog_id); contentView.addView(mentionContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 110, Gravity.LEFT | Gravity.BOTTOM)); contentPreviewViewerDelegate = new ContentPreviewViewer.ContentPreviewViewerDelegate() { @@ -6703,7 +6704,7 @@ public void openSet(TLRPC.InputStickerSet set, boolean clearsInputField) { TLRPC.TL_inputStickerSetID inputStickerSet = new TLRPC.TL_inputStickerSetID(); inputStickerSet.access_hash = set.access_hash; inputStickerSet.id = set.id; - StickersAlert alert = new StickersAlert(getParentActivity(), ChatActivity.this, inputStickerSet, null, chatActivityEnterView, themeDelegate); + StickersAlert alert = new StickersAlert(getParentActivity(), ChatActivity.this, inputStickerSet, null, chatActivityEnterView, themeDelegate, false); alert.setCalcMandatoryInsets(isKeyboardVisible()); alert.setClearsInputField(clearsInputField); showDialog(alert); @@ -6731,6 +6732,13 @@ public long getDialogId() { Object object = mentionContainer.getAdapter().getItem(position); int start = mentionContainer.getAdapter().getResultStartPosition(); int len = mentionContainer.getAdapter().getResultLength(); + if (mentionContainer.getAdapter().isLocalHashtagHint(position)) { + chatActivityEnterView.replaceWithText(start, len, mentionContainer.getAdapter().getHashtagHint() + "@" + ChatObject.getPublicUsername(currentChat) + " ", false); + return; + } else if (mentionContainer.getAdapter().isGlobalHashtagHint(position)) { + chatActivityEnterView.replaceWithText(start, len, mentionContainer.getAdapter().getHashtagHint() + " ", false); + return; + } if (object instanceof QuickRepliesController.QuickReply) { if (!getUserConfig().isPremium()) { showDialog(new PremiumFeatureBottomSheet(this, getContext(), currentAccount, true, PremiumPreviewFragment.PREMIUM_FEATURE_BUSINESS_QUICK_REPLIES, false, null)); @@ -7109,7 +7117,7 @@ protected void onDetachedFromWindow() { LinearLayoutManager messagesSearchLayoutManager = new LinearLayoutManager(context); messagesSearchLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); messagesSearchListView.setLayoutManager(messagesSearchLayoutManager); - messagesSearchListView.setAdapter(messagesSearchAdapter = new MessagesSearchAdapter(context, themeDelegate, searchType, dialog_id == getUserConfig().getClientUserId())); + messagesSearchListView.setAdapter(messagesSearchAdapter = new MessagesSearchAdapter(context, this, themeDelegate, searchType, dialog_id == getUserConfig().getClientUserId())); checkHashtagStories(true); DefaultItemAnimator itemAnimator = new DefaultItemAnimator(); itemAnimator.setSupportsChangeAnimations(false); @@ -7121,10 +7129,13 @@ protected void onDetachedFromWindow() { messagesSearchListView.setOnItemClickListener((view, position) -> { if (chatMode == MODE_SEARCH) { Object obj = messagesSearchAdapter.getItem(position); - if (position == 0 && messagesSearchAdapter.containsStories) { + if (position == 0 && messagesSearchAdapter.containsStories && messagesSearchAdapter.storiesList != null) { Bundle args = new Bundle(); args.putInt("type", MediaActivity.TYPE_STORIES_SEARCH); args.putString("hashtag", messagesSearchAdapter.storiesList.query); + if (messagesSearchAdapter.storiesList.username != null) { + args.putString("username", messagesSearchAdapter.storiesList.username); + } args.putInt("storiesCount", messagesSearchAdapter.storiesList.getCount()); presentFragment(new MediaActivity(args, null)); } else if (obj instanceof MessageObject) { @@ -7192,6 +7203,11 @@ public boolean onTouchEvent(MotionEvent ev) { return false; } + @Override + protected boolean canScroll(MotionEvent e) { + return hashtagSearchTabs != null && hashtagSearchTabs.shown(); + } + @Override protected void onTabAnimationUpdate(boolean manual) { super.onTabAnimationUpdate(manual); @@ -7214,9 +7230,11 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); if (child instanceof ChatActivityContainer) { - int p = actionBar.getHeight() + hashtagSearchTabs.getHeight(); + int p = actionBar.getHeight(); // + hashtagSearchTabs.getHeight(); ChatActivity chatActivity = ((ChatActivityContainer) child).chatActivity; - chatActivity.contentView.setPadding(0, p, 0, 0); + if (chatActivity.contentView != null) { + chatActivity.contentView.setPadding(0, p, 0, 0); + } } } } @@ -7268,6 +7286,9 @@ public void openHashtagSearch(String hashtag) { public void bindView(View view, int position, int viewType) { if (view instanceof ChatActivityContainer) { ((ChatActivityContainer) view).chatActivity.updateSearchingHashtag(searchingHashtag); + } else if (view instanceof PublicStoriesList) { + ((PublicStoriesList) view).setTabs(parentChatActivity != null ? parentChatActivity.hashtagSearchTabs.isShown() : hashtagSearchTabs.isShown()); + ((PublicStoriesList) view).setQuery("", searchingHashtag); } } @@ -7497,6 +7518,9 @@ public void onAnimationEnd(Animator animation) { @Override protected void onLineCountChanged(int oldLineCount, int newLineCount) { if (chatActivityEnterView != null) { + if (chatListView != null && (searchExpandProgress > 0 || actionBar != null && actionBar.isActionModeShowed())) { + return; + } shouldAnimateEditTextWithBounds = true; messageEditTextPredrawHeigth = messageEditText.getMeasuredHeight(); messageEditTextPredrawScrollY = messageEditText.getScrollY(); @@ -7504,6 +7528,16 @@ protected void onLineCountChanged(int oldLineCount, int newLineCount) { chatActivityEnterViewAnimateFromTop = chatActivityEnterView.getBackgroundTop(); } } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (chatListView != null && actionBar != null && actionBar.isActionModeShowed()) { + chatListView.setTranslationY(chatActivityEnterView.getMeasuredHeight() - AndroidUtilities.dp(51)); + } else if (chatListView != null && searchExpandProgress > 0) { + chatListView.setTranslationY(searchExpandProgress * (chatActivityEnterView.getMeasuredHeight() - AndroidUtilities.dp(searchContainerHeight))); + } + } }; chatActivityEnterView.getEditField().adaptiveCreateLinkDialog = true; if (chatMode == MODE_EDIT_BUSINESS_LINK) { @@ -7656,11 +7690,24 @@ public void setVisibility(int visibility) { replyLayout.setOnClickListener(v -> { if (fieldPanelShown == 1 && editingMessageObject != null) { if (editingMessageObject.canEditMedia() && editingMessageObjectReqId == 0) { - if (chatAttachAlert == null) { - createChatAttachView(); - } - chatAttachAlert.setEditingMessageObject(editingMessageObject); - openAttachMenu(); + Utilities.Callback open = type -> { + if (chatAttachAlert == null) { + createChatAttachView(); + } + chatAttachAlert.setEditingMessageObject(type, editingMessageObject); + openAttachMenu(); + }; + open.run(ChatAttachAlert.EDITMEDIA_TYPE_ANY); +// if (editingMessageObject.hasValidGroupId()) { +// open.run(-1); +// } else { +// ItemOptions.makeOptions(ChatActivity.this, replyLayout, true) +// .add(editingMessageObject.isMediaEmpty() ? R.drawable.msg_addphoto : R.drawable.msg_photos, getString(editingMessageObject.isMediaEmpty() ? R.string.MessageAddPhotoVideo : R.string.MessageReplacePhotoVideo), () -> open.run(ChatAttachAlert.EDITMEDIA_TYPE_PHOTOVIDEO)) +// .add(editingMessageObject.isMediaEmpty() ? R.drawable.menu_sendfile_plus : R.drawable.msg_sendfile, getString(editingMessageObject.isMediaEmpty() ? R.string.MessageAddFile : R.string.MessageReplaceFile), () -> open.run(ChatAttachAlert.EDITMEDIA_TYPE_FILE)) +// .add(editingMessageObject.isMediaEmpty() ? R.drawable.files_music : R.drawable.files_music, getString(editingMessageObject.isMediaEmpty() ? R.string.MessageAddMusic : R.string.MessageReplaceMusic), () -> open.run(ChatAttachAlert.EDITMEDIA_TYPE_MUSIC)) +// .setDrawScrim(false).setGravity(Gravity.LEFT).forceTop(true).translate(0, dp(48)) +// .show(); +// } } else { scrollToMessageId(editingMessageObject.getId(), 0, true, 0, true, 0); } @@ -8241,7 +8288,7 @@ protected void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { }; hashtagHistoryView.setVisibility(View.GONE); hashtagHistoryView.setAlpha(0f); - contentView.addView(hashtagHistoryView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 40, 0, 0)); + contentView.addView(hashtagHistoryView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0)); contentView.addView(fireworksOverlay = new FireworksOverlay(context), LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); @@ -8540,6 +8587,9 @@ private void hideHints() { if (savedMessagesTagHint != null && savedMessagesTagHint.shown()) { savedMessagesTagHint.hide(); } + if (videoConversionTimeHint != null && videoConversionTimeHint.shown()) { + videoConversionTimeHint.hide(); + } if (chatActivityEnterView != null) { chatActivityEnterView.hideHints(); } @@ -9286,6 +9336,29 @@ protected void dispatchDraw(Canvas canvas) { }); } + private void createTopPanel2() { + if (contentView == null || topChatPanelView2 != null || getContext() == null) { + return; + } + + topChatPanelView2 = new BlurredFrameLayout(getContext(), contentView); + topChatPanelView2.backgroundColor = getThemedColor(Theme.key_chat_topPanelBackground); + topChatPanelView2.backgroundPaddingBottom = AndroidUtilities.dp(2); + topChatPanelView2.setPadding(0, 0, 0, dp(2)); + topChatPanelView2.setTag(1); + topChatPanelView2Offset = -AndroidUtilities.dp(50); + invalidateChatListViewTopPadding(); + topChatPanelView2.setClickable(true); + topChatPanelView2.setVisibility(View.GONE); + topChatPanelView2.setBackgroundResource(R.drawable.blockpanel); + topChatPanelView2.getBackground().setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_topPanelBackground), PorterDuff.Mode.MULTIPLY)); + int index = 8; + if (pinnedMessageView != null && pinnedMessageView.getParent() == contentView) { + index = contentView.indexOfChild(pinnedMessageView) + 1; + } + contentView.addView(topChatPanelView2, index, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 50, Gravity.TOP | Gravity.LEFT)); + } + private void createTranslateButton() { if (translateButton != null || getContext() == null) { return; @@ -9316,6 +9389,19 @@ protected void onCloseClick() { topChatPanelView.addView(translateButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 36, Gravity.LEFT | Gravity.BOTTOM, 0, 0, 0, 2)); } + private void createBotAdView() { + if (botAdView != null || getContext() == null) { + return; + } + + createTopPanel2(); + if (topChatPanelView2 == null) { + return; + } + botAdView = new BotAdView(getContext(), themeDelegate); + topChatPanelView2.addView(botAdView); + } + private void createBizBotButton() { if (bizBotButton != null || getContext() == null) { return; @@ -9325,24 +9411,7 @@ private void createBizBotButton() { if (topChatPanelView == null) { return; } - bizBotButton = new BusinessBotButton(getContext(), this, themeDelegate) { -// @Override -// protected void onButtonClick() { -// if (getUserConfig().isPremium()) { -// getMessagesController().getTranslateController().toggleTranslatingDialog(getDialogId()); -// } else { -// MessagesController.getNotificationsSettings(currentAccount).edit().putInt("dialog_show_translate_count" + getDialogId(), 14).commit(); -// showDialog(new PremiumFeatureBottomSheet(ChatActivity.this, PremiumPreviewFragment.PREMIUM_FEATURE_TRANSLATIONS, false)); -// } -// updateTopPanel(true); -// } -// -// @Override -// protected void onCloseClick() { -// MessagesController.getNotificationsSettings(currentAccount).edit().putInt("dialog_show_translate_count" + getDialogId(), 140).commit(); -// updateTopPanel(true); -// } - }; + bizBotButton = new BusinessBotButton(getContext(), this, themeDelegate); topChatPanelView.addView(bizBotButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.BOTTOM, 0, 0, 0, 2)); } @@ -9586,7 +9655,7 @@ public void onDraw(Canvas canvas) { @Override protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { if (child == searchCountText) { - int leftMargin = 14; + int leftMargin = 18; if (searchCalendarButton != null && searchCalendarButton.getVisibility() != GONE) { leftMargin += 48; } @@ -9823,6 +9892,7 @@ private void dimBehindView(float value, boolean blur, boolean hidePagedownButton if (scrimViewAlphaAnimator != null) { scrimViewAlphaAnimator.cancel(); } + scrimProgressDirection = true; animators.add(scrimPaintAlphaAnimator = ValueAnimator.ofFloat(0, value)); if (blur) { @@ -9840,6 +9910,7 @@ private void dimBehindView(float value, boolean blur, boolean hidePagedownButton } } else { scrimViewProgress = scrimPaintAlpha / max; + scrimProgressDirection = false; animators.add(scrimPaintAlphaAnimator = ValueAnimator.ofFloat(scrimPaintAlpha, 0)); } scrimPaintAlphaAnimator.addUpdateListener(a -> { @@ -9861,7 +9932,8 @@ private void dimBehindView(float value, boolean blur, boolean hidePagedownButton } } scrimAnimatorSet.playTogether(animators); - scrimAnimatorSet.setDuration(enable ? 150 : 220); + scrimAnimatorSet.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + scrimAnimatorSet.setDuration(320); final ChatMessageCell cell = scrimView instanceof ChatMessageCell ? (ChatMessageCell) scrimView : null; scrimAnimatorSet.addListener(new AnimatorListenerAdapter() { @Override @@ -11037,6 +11109,8 @@ private void updateChatListViewTopPadding() { } float topPanelViewH = Math.max(0, topChatPanelView != null && topChatPanelView.getVisibility() == View.VISIBLE ? (topChatPanelView.getLayoutParams().height - AndroidUtilities.dp(2)) : 0); topPanelViewH += Math.max(-topPanelViewH, topChatPanelViewOffset); + float topPanelView2H = Math.max(0, topChatPanelView2 != null && topChatPanelView2.getVisibility() == View.VISIBLE ? (topChatPanelView2.getLayoutParams().height - AndroidUtilities.dp(2)) : 0); + topPanelView2H += Math.max(-topPanelView2H, topChatPanelView2Offset); float pinnedViewH = 0; if (pinnedMessageView != null && pinnedMessageView.getVisibility() == View.VISIBLE) { pinnedViewH = Math.max(0, AndroidUtilities.dp(48) + pinnedMessageEnterOffset); @@ -11056,7 +11130,7 @@ private void updateChatListViewTopPadding() { pendingViewH = Math.max(0, pendingRequestsView.getHeight() + pendingRequestsDelegate.getViewEnterOffset() - AndroidUtilities.dp(4)); } float oldPadding = chatListViewPaddingTop; - chatListViewPaddingTop = AndroidUtilities.dp(4) + contentPaddingTop + (paddingTopHeight = topPanelViewH + pinnedViewH + pendingViewH); + chatListViewPaddingTop = AndroidUtilities.dp(4) + contentPaddingTop + (paddingTopHeight = topPanelViewH + pinnedViewH + pendingViewH + topPanelView2H); chatListViewPaddingTop += blurredViewTopOffset; chatListViewPaddingVisibleOffset = 0; chatListViewPaddingTop += contentPanTranslation + bottomPanelTranslationY; @@ -11164,6 +11238,9 @@ private void invalidateChatListViewTopPadding() { pinnedMessageView.setTranslationY(translation); translation += AndroidUtilities.dp(48); } + int topPanel2Height = topChatPanelView2 != null && topChatPanelView2.getVisibility() == View.VISIBLE ? ((topChatPanelView2.getLayoutParams() == null ? AndroidUtilities.dp(50) : topChatPanelView2.getLayoutParams().height) - AndroidUtilities.dp(2)) : 0; + topPanel2Height = topPanel2Height + (int) Math.max(-topPanel2Height, topChatPanelView2Offset); + translation += Math.max(0, topPanel2Height); View pendingRequestsView = pendingRequestsDelegate != null ? pendingRequestsDelegate.getView() : null; if (pendingRequestsView != null) { translation += pendingRequestsDelegate.getViewEnterOffset(); @@ -11189,6 +11266,9 @@ private void invalidateChatListViewTopPadding() { if (topChatPanelView != null) { topChatPanelView.setTranslationY(contentPanTranslation + p + contentPaddingTop + topChatPanelViewOffset); } + if (topChatPanelView2 != null) { + topChatPanelView2.setTranslationY(contentPanTranslation + p + contentPaddingTop + topChatPanelView2Offset + topPanelHeight + Math.max(0, topChatPanelViewOffset) + Math.max(0, pinnedMessageView != null ? dp(48) + pinnedMessageEnterOffset : 0)); + } if (alertView != null && alertView.getVisibility() == View.VISIBLE) { alertView.setTranslationY(contentPanTranslation + p + contentPaddingTop - AndroidUtilities.dp(50) * (1f - alertViewEnterProgress)); } @@ -11201,6 +11281,13 @@ private void invalidateChatListViewTopPadding() { if (undoView != null) { undoView.setAdditionalTranslationY(chatActivityEnterView.getHeightWithTopView() - chatActivityEnterView.getAnimatedTop()); } + if (messagesSearchListContainer != null) { + messagesSearchListContainer.setTranslationY(getHashtagTabsHeight() + contentPanTranslation); + messagesSearchListContainer.setPadding(0, (actionBarSearchTags != null && actionBarSearchTags.shown() ? actionBarSearchTags.getHeight() : 0), 0, getHashtagTabsHeight()); + } + if (hashtagHistoryView != null) { + hashtagHistoryView.setTranslationY(getHashtagTabsHeight() + contentPanTranslation); + } } private TextureView createTextureView(boolean add) { @@ -11686,25 +11773,25 @@ public void jumpToDate(int date) { private boolean approved; public void processInlineBotWebView(TLRPC.TL_inlineBotWebView object) { final Runnable open = () -> { - final WebViewRequestProps props = WebViewRequestProps.of(currentAccount, currentUser != null ? currentUser.id : currentChat.id, mentionContainer.getAdapter().getFoundContextBot().id, object.text, object.url, BotWebViewSheet.TYPE_SIMPLE_WEB_VIEW_BUTTON, 0, false, null, false, null, null, BotWebViewSheet.FLAG_FROM_INLINE_SWITCH, false); + final WebViewRequestProps props = WebViewRequestProps.of(currentAccount, currentUser != null ? currentUser.id : currentChat.id, mentionContainer.getAdapter().getFoundContextBot().id, object.text, object.url, BotWebViewSheet.TYPE_SIMPLE_WEB_VIEW_BUTTON, 0, false, null, false, null, null, BotWebViewSheet.FLAG_FROM_INLINE_SWITCH, false, false); if (LaunchActivity.instance != null && LaunchActivity.instance.getBottomSheetTabs() != null && LaunchActivity.instance.getBottomSheetTabs().tryReopenTab(props) != null) { return; } - if (AndroidUtilities.isTablet()) { +// if (AndroidUtilities.isTablet() || true) { BotWebViewSheet webViewSheet = new BotWebViewSheet(getContext(), getResourceProvider()); webViewSheet.setDefaultFullsize(false); webViewSheet.setNeedsContext(true); webViewSheet.setParentActivity(getParentActivity()); webViewSheet.requestWebView(ChatActivity.this, props); webViewSheet.show(); - } else { - BotWebViewAttachedSheet webViewSheet = createBotViewer(); - webViewSheet.setDefaultFullsize(false); - webViewSheet.setNeedsContext(true); - webViewSheet.setParentActivity(getParentActivity()); - webViewSheet.requestWebView(ChatActivity.this, props); - webViewSheet.show(); - } +// } else { +// BotWebViewAttachedSheet webViewSheet = createBotViewer(); +// webViewSheet.setDefaultFullsize(false); +// webViewSheet.setNeedsContext(true); +// webViewSheet.setParentActivity(getParentActivity()); +// webViewSheet.requestWebView(ChatActivity.this, props); +// webViewSheet.show(); +// } }; if (approved) { @@ -11777,6 +11864,9 @@ public void didPressedButton(int button, boolean arg, boolean notify, int schedu return; } editingMessageObject = chatAttachAlert.getEditingMessageObject(); + if (editingMessageObject != null && editingMessageObject.messageOwner != null) { + editingMessageObject.messageOwner.invert_media = invertMedia; + } if (button == 8 || button == 7 || button == 4 && !chatAttachAlert.getPhotoLayout().getSelectedPhotos().isEmpty()) { if (button != 8) { chatAttachAlert.dismiss(true); @@ -12046,7 +12136,7 @@ private void showMessagesSearchListView(boolean show) { } } messagesSearchListContainer.setTag(show ? 1 : null); - messagesSearchListContainer.setPadding(0, (hashtagSearchTabs != null && hashtagSearchTabs.shown() ? hashtagSearchTabs.getHeight() : 0) + (actionBarSearchTags != null && actionBarSearchTags.shown() ? actionBarSearchTags.getHeight() : 0), 0, 0); + messagesSearchListContainer.setPadding(0, (actionBarSearchTags != null && actionBarSearchTags.shown() ? actionBarSearchTags.getHeight() : 0), 0, getHashtagTabsHeight()); messagesSearchListViewAnimation = new AnimatorSet(); messagesSearchListViewAnimation.playTogether(ObjectAnimator.ofFloat(messagesSearchListContainer, View.ALPHA, show ? 1.0f : 0.0f)); messagesSearchListViewAnimation.setInterpolator(CubicBezierInterpolator.EASE_IN); @@ -12098,6 +12188,9 @@ public boolean playFirstUnreadVoiceMessage() { } private void openScheduledMessages() { + openScheduledMessages(0, false); + } + private void openScheduledMessages(int fromMessageId, boolean showConvertToast) { if (parentLayout == null || parentLayout.getLastFragment() != this) { return; } @@ -12110,6 +12203,10 @@ private void openScheduledMessages() { bundle.putLong("user_id", currentUser.id); } bundle.putInt("chatMode", MODE_SCHEDULED); + if (showConvertToast) { + bundle.putInt("converting_toast_from", fromMessageId); + bundle.putBoolean("converting_toast", true); + } ChatActivity fragment = new ChatActivity(bundle); if (isTopic) { ForumUtilities.applyTopic(fragment, MessagesStorage.TopicKey.of(getDialogId(), getTopicId())); @@ -13744,15 +13841,15 @@ public void showFieldPanel(boolean show, MessageObject messageObjectToReply, Mes } chatActivityEnterTopView.setEditMode(true); } else { - replyIconImageView.setImageResource(R.drawable.group_edit); + replyIconImageView.setImageResource(canEditMedia ? R.drawable.nav_edit_attach : R.drawable.group_edit); replyIconImageView.setContentDescription(LocaleController.getString(R.string.AccDescrEditing)); - if (mediaEmpty) { + if (mediaEmpty || canEditMedia) { replyNameTextView.setText(LocaleController.getString(R.string.EditMessage)); } else { replyNameTextView.setText(LocaleController.getString(R.string.EditCaption)); } if (canEditMedia) { - replyObjectTextView.setText(LocaleController.getString(R.string.EditMessageMedia)); + replyObjectTextView.setText(LocaleController.getString(mediaEmpty ? R.string.AddMessageMedia : R.string.EditMessageMedia)); } else if (messageObjectToEdit.messageText != null || messageObjectToEdit.caption != null) { CharSequence mess = messageObjectToEdit.caption != null ? messageObjectToEdit.caption : messageObjectToEdit.messageText; if (mess.length() > 150) { @@ -15803,11 +15900,11 @@ protected void onPanTranslationUpdate(float y, float progress, boolean keyboardV emptyViewContainer.setTranslationY(y / 2); } if (hashtagHistoryView != null) { - hashtagHistoryView.setTranslationY(y); + hashtagHistoryView.setTranslationY(getHashtagTabsHeight() + y); hashtagHistoryView.emptyView.setTranslationY(-y / 2); } if (messagesSearchListContainer != null) { - messagesSearchListContainer.setTranslationY(y); + messagesSearchListContainer.setTranslationY(getHashtagTabsHeight() + y); } if (hashtagSearchEmptyView != null) { hashtagSearchEmptyView.setTranslationY(-y / 2); @@ -15817,7 +15914,7 @@ protected void onPanTranslationUpdate(float y, float progress, boolean keyboardV if (page instanceof ChatActivityContainer) { ChatActivity child = ((ChatActivityContainer) page).chatActivity; if (child.messagesSearchListContainer != null) { - child.messagesSearchListContainer.setTranslationY(y); + child.messagesSearchListContainer.setTranslationY(getHashtagTabsHeight() + y); } if (child.hashtagSearchEmptyView != null) { child.hashtagSearchEmptyView.setTranslationY(-y / 2); @@ -16295,7 +16392,7 @@ private void drawChildElement(Canvas canvas, float listTop, ChatMessageCell cell final float scrimProgress = scrimPaintAlpha * scrimViewAlpha / 0.2f; canvas.save(); - cell.drawScrimReaction(canvas, scrimViewReaction, scrimProgress); + cell.drawScrimReaction(canvas, scrimViewReaction, scrimProgress, scrimProgressDirection); canvas.restore(); canvas.restore(); @@ -16597,7 +16694,7 @@ protected void dispatchDraw(Canvas canvas) { } canvas.clipRect(viewClipLeft, viewClipTop, viewClipRight, viewClipBottom); canvas.translate(chatListView.getLeft() + child.getX(), chatListView.getY() + child.getY()); - cell.drawScrimReaction(canvas, scrimViewReaction, scrimProgress); + cell.drawScrimReaction(canvas, scrimViewReaction, scrimProgress, scrimProgressDirection); canvas.restore(); canvas.save(); @@ -16717,6 +16814,9 @@ protected void dispatchDraw(Canvas canvas) { if (savedMessagesTagHint != null && savedMessagesTagHint.getVisibility() == View.VISIBLE) { super.drawChild(canvas, savedMessagesTagHint, SystemClock.uptimeMillis()); } + if (videoConversionTimeHint != null && videoConversionTimeHint.getVisibility() == View.VISIBLE) { + super.drawChild(canvas, videoConversionTimeHint, SystemClock.uptimeMillis()); + } if (botMessageHint != null && botMessageHint.getVisibility() == View.VISIBLE) { super.drawChild(canvas, botMessageHint, SystemClock.uptimeMillis()); } @@ -16945,7 +17045,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { child.measure(contentWidthSpec, contentHeightSpec); } else if (child == messagesSearchListContainer) { int contentWidthSpec = View.MeasureSpec.makeMeasureSpec(widthSize, View.MeasureSpec.EXACTLY); - int contentHeightSpec = View.MeasureSpec.makeMeasureSpec(allHeight - (isInsideContainer ? 0 : actionBarHeight) - AndroidUtilities.dp(48) - ((isInsideContainer && parentChatActivity != null) ? contentPaddingTop : 0), View.MeasureSpec.EXACTLY); + int contentHeightSpec = View.MeasureSpec.makeMeasureSpec(allHeight - (isInsideContainer ? 0 : actionBarHeight) - AndroidUtilities.dp(48), View.MeasureSpec.EXACTLY); child.measure(contentWidthSpec, contentHeightSpec); } else if (chatActivityEnterView.isPopupView(child)) { int height = chatActivityEnterView.getPopupViewHeight(child); @@ -17193,7 +17293,7 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { childTop = 0; } else if (child == messagesSearchListContainer) { if (isInsideContainer && parentChatActivity != null) { - childTop += contentPaddingTop; + childTop += chatMode != MODE_SEARCH ? contentPaddingTop : actionBar.getMeasuredHeight(); } } child.layout(childLeft, childTop, childLeft + width, childTop + height); @@ -18333,6 +18433,9 @@ public void openVideoEditor(String videoPath, String caption) { MediaController.PhotoEntry entry = new MediaController.PhotoEntry(0, 0, 0, videoPath, 0, true, 0, 0, 0); entry.caption = caption; cameraPhoto.add(entry); + if (PhotoViewer.getInstance().isVisible()) { + PhotoViewer.getInstance().closePhoto(false, false); + } PhotoViewer.getInstance().openPhotoForSelect(cameraPhoto, 0, 0, false, new PhotoViewer.EmptyPhotoViewerProvider() { @Override public ImageReceiver.BitmapHolder getThumbForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index) { @@ -18645,6 +18748,9 @@ private void fillEditingMediaWithCaption(CharSequence caption, ArrayList(); } else if (chatActivityEnterView != null) { editingMessageObject.editingMessage = chatActivityEnterView.getFieldText(); if (editingMessageObject.editingMessage == null && !TextUtils.isEmpty(editingMessageObject.messageOwner.message)) { @@ -18654,6 +18760,9 @@ private void fillEditingMediaWithCaption(CharSequence caption, ArrayList { + fillEditingMediaWithCaption(null, null); + sendUriAsDocument(data.getData(), notify, scheduleDate); + afterMessageSend(); + }, themeDelegate); + return; + } else if (data.getClipData() != null) { + if (chatAttachAlert != null) { + chatAttachAlert.dismiss(); + } + AlertsCreator.createScheduleDatePickerDialog(getParentActivity(), dialog_id, (notify, scheduleDate) -> { + fillEditingMediaWithCaption(null, null); + ClipData clipData = data.getClipData(); + for (int i = 0; i < clipData.getItemCount(); i++) { + sendUriAsDocument(clipData.getItemAt(i).getUri(), notify, scheduleDate); + } + afterMessageSend(); + }, themeDelegate); + return; + } else { + showAttachmentError(); } } else { - showAttachmentError(); + fillEditingMediaWithCaption(null, null); + if (data.getData() != null) { + sendUriAsDocument(data.getData()); + } else if (data.getClipData() != null) { + ClipData clipData = data.getClipData(); + for (int i = 0; i < clipData.getItemCount(); i++) { + sendUriAsDocument(clipData.getItemAt(i).getUri()); + } + } else { + showAttachmentError(); + } } if (chatAttachAlert != null) { chatAttachAlert.dismiss(); @@ -19276,6 +19415,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { TLRPC.MessageAction dropPhotoAction = null; boolean createdWas = false; boolean moveCurrentDateObject = false; + boolean moveCurrentConversionObject = false; boolean scrolledToUnread = false; for (int a = 0, N = messArr.size(); a < N; a++) { MessageObject obj = messArr.get(N - a - 1); @@ -19389,7 +19529,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { messagesDict[loadIndex].put(messageId, obj); ArrayList dayArray = messagesByDays.get(obj.dateKey); - final boolean addDateObjects = chatMode != MODE_QUICK_REPLIES; + final boolean addDateObjects = chatMode != MODE_QUICK_REPLIES;// && chatMode != MODE_SCHEDULED; if (dayArray == null) { dayArray = new ArrayList<>(); messagesByDays.put(obj.dateKey, dayArray); @@ -19400,22 +19540,21 @@ public void didReceivedNotification(int id, int account, final Object... args) { if (obj.messageOwner.date == 0x7ffffffe) { dateMsg.message = LocaleController.getString(R.string.MessageScheduledUntilOnline); } else { - dateMsg.message = LocaleController.formatString("MessageScheduledOn", R.string.MessageScheduledOn, LocaleController.formatDateChat(obj.messageOwner.date, true)); + dateMsg.message = LocaleController.formatString(R.string.MessageScheduledOn, LocaleController.formatDateChat(obj.messageOwner.date, true)); } } else { dateMsg.message = LocaleController.formatDateChat(obj.messageOwner.date); } dateMsg.id = 0; - Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(((long) obj.messageOwner.date) * 1000); - calendar.set(Calendar.HOUR_OF_DAY, 0); - calendar.set(Calendar.MINUTE, 0); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MILLISECOND, 0); - if (chatMode == MODE_SCHEDULED && obj.messageOwner.date == 0x7ffffffe) { dateMsg.date = 0x7ffffffe; } else { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(((long) obj.messageOwner.date) * 1000); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); dateMsg.date = (int) (calendar.getTimeInMillis() / 1000); } MessageObject dateObj = new MessageObject(currentAccount, dateMsg, false, false); @@ -19519,6 +19658,45 @@ public void didReceivedNotification(int id, int account, final Object... args) { messages.add(messages.size() - 1, obj); } } + + if (obj.messageOwner != null && obj.messageOwner.video_processing_pending && ( + !obj.hasValidGroupId() || (a + 1) >= messArr.size() || obj.getGroupIdForUse() != messArr.get(a + 1).getGroupIdForUse() + )) { + TLRPC.Message msg = new TLRPC.TL_message(); + msg.message = getString(R.string.VideoConversionAction); + if (chatMode == MODE_SCHEDULED && obj.messageOwner.date == 0x7ffffffe) { + msg.date = 0x7ffffffe; + } else { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(((long) obj.messageOwner.date) * 1000); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + msg.date = (int) (calendar.getTimeInMillis() / 1000); + } + MessageObject dateObj = new MessageObject(currentAccount, msg, false, false); + dateObj.type = MessageObject.TYPE_DATE; + dateObj.contentType = 1; + dateObj.isDateObject = true; + dateObj.isVideoConversionObject = true; + dateObj.stableId = getStableIdForConversionObject(obj.getId()); + if (load_type == 1) { + messages.add(0, dateObj); + } else { + if (!messages.isEmpty()) { + messages.get(messages.size() - 1).stableId = lastStableId++; + } + if (reversed || !addDateObjects) { + messages.add(dateObj); + } else { + messages.add(messages.size() - 1, dateObj); + } + } + conversionMessages.put(obj.getId(), dateObj); + newRowsCount++; + } + MessageObject prevObj; if (currentEncryptedChat == null) { if (createUnreadMessageAfterId != 0 && load_type != 1 && a + 1 < messArr.size()) { @@ -20033,7 +20211,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { } } - StickersAlert alert = new StickersAlert(getParentActivity(), ChatActivity.this, inputStickerSet, null, chatActivityEnterView, themeDelegate); + StickersAlert alert = new StickersAlert(getParentActivity(), ChatActivity.this, inputStickerSet, null, chatActivityEnterView, themeDelegate, false); alert.setOnShowListener(dialog -> { if (args.length > 2 && args[2] instanceof TLRPC.Document) { TLRPC.Document stickerDocument = (TLRPC.Document) args[2]; @@ -20154,6 +20332,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { updateTopPanel(true); } } else if (id == NotificationCenter.didReceiveNewMessages) { + FileLog.d("ChatActivity didReceiveNewMessages start"); long did = (Long) args[0]; ArrayList arr = (ArrayList) args[1]; if (isInsideContainer) return; @@ -20161,11 +20340,12 @@ public void didReceivedNotification(int id, int account, final Object... args) { boolean scheduled = (Boolean) args[2]; int mode = (Integer) args[3]; if (mode != chatMode && chatMode != MODE_SAVED) { - if (chatMode != MODE_SCHEDULED && mode == MODE_SCHEDULED && !isPaused && messagePreviewParams == null) { + if (chatMode != MODE_SCHEDULED && mode == MODE_SCHEDULED && !isPaused && LaunchActivity.getSafeLastFragment() == this && messagePreviewParams == null) { if (!arr.isEmpty() && arr.get(0).getId() < 0) { - openScheduledMessages(); + openScheduledMessages(arr.get(0).getId(), arr.get(0).messageOwner != null && arr.get(0).messageOwner.video_processing_pending); } } + FileLog.d("ChatActivity didReceiveNewMessages return: opened scheduled messages"); return; } processNewMessages(arr); @@ -20178,6 +20358,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { } checkWaitingForReplies(); } + FileLog.d("ChatActivity didReceiveNewMessages return: done"); } else if (id == NotificationCenter.didLoadSendAsPeers) { loadSendAsPeers(true); } else if (id == NotificationCenter.didLoadSponsoredMessages) { @@ -20369,6 +20550,14 @@ public void didReceivedNotification(int id, int account, final Object... args) { messages.remove(b); b--; messagesDict[0].remove(mid); + MessageObject conversionMsg = conversionMessages.get(mid); + if (conversionMsg != null) { + conversionMessages.remove(mid); + if (b >= 0 && b < messages.size()) { + messages.remove(b); + b--; + } + } ArrayList dayArr = messagesByDays.get(obj.dateKey); if (dayArr != null) { dayArr.remove(obj); @@ -20429,8 +20618,57 @@ public void didReceivedNotification(int id, int account, final Object... args) { } ArrayList markAsDeletedMessages = (ArrayList) args[0]; long channelId = (Long) args[1]; + boolean update = args.length > 2 && (boolean) args[2]; boolean sent = args.length > 3 && (boolean) args[3]; - processDeletedMessages(markAsDeletedMessages, channelId, sent); + boolean movedToScheduled = args.length > 4 && (boolean) args[4]; + int scheduledMessageId = args.length > 5 ? (int) args[5] : 0; + ArrayList sentMessages = null; + if (args.length > 6) sentMessages = (ArrayList) args[6]; + final ArrayList messages = new ArrayList<>(); + MessageObject conversionMessage = null; + boolean conversion = false; + for (int msg_id : markAsDeletedMessages) { + MessageObject msg = messagesDict[0].get(msg_id); + if (msg != null) { + if (msg.messageOwner != null && msg.messageOwner.video_processing_pending) { + if (conversionMessage == null) conversionMessage = msg; + conversion = true; + } + messages.add(msg); + } + } + if (scheduleNowDialog != null && selectedObject != null && markAsDeletedMessages.contains(selectedObject.getId())) { + scheduleNowDialog.dismiss(); + scheduleNowDialog = null; + } + processDeletedMessages(markAsDeletedMessages, channelId, sent, !movedToScheduled); + if (movedToScheduled && chatMode != ChatActivity.MODE_SCHEDULED) { + getMessagesController().forceNoReload(dialog_id, ChatActivity.MODE_SCHEDULED); + openScheduledMessages(scheduledMessageId, true); + } else if (chatMode == ChatActivity.MODE_SCHEDULED && update && conversion && LaunchActivity.getSafeLastFragment() == this && isFullyVisible && sentMessages != null && !sentMessages.isEmpty() && parentLayout != null) { + if (this.messages.isEmpty() || markAsDeletedMessages.contains(convertingToastMessageId)) { + finishFragment(); + if (parentLayout == null) return; + BulletinFactory.of(parentLayout.getBackgroundFragment()) + .createSimpleBulletin(R.raw.contact_check, getString(R.string.VideoConversionDone)) + .show(true); + } else { + int messageId = sentMessages.get(0); + BulletinFactory.of(this) + .createSimpleBulletin(R.raw.contact_check, getString(R.string.VideoConversionDone), getString(R.string.VideoConversionDoneView), () -> { + if (parentLayout == null) return; + BaseFragment fragment = parentLayout.getBackgroundFragment(); + if (fragment instanceof ChatActivity && ((ChatActivity) fragment).getDialogId() == dialog_id) { + finishFragment(); + ((ChatActivity) fragment).scrollToMessageId(messageId, 0, true, 0, true, 0); + } else { + presentFragment(ChatActivity.of(dialog_id, messageId)); + } + }) + .setDuration(3500) + .show(true); + } + } } else if (id == NotificationCenter.quickRepliesDeleted) { if (chatMode != MODE_QUICK_REPLIES) return; if ((Long) args[1] != getQuickReplyId()) return; @@ -20463,6 +20701,13 @@ public void didReceivedNotification(int id, int account, final Object... args) { if (removed != null) { int index = messages.indexOf(removed); messages.remove(index); + MessageObject conversionMsg = conversionMessages.get(removed.getId()); + if (conversionMsg != null) { + conversionMessages.remove(removed.getId()); + if (index >= 0 && index < messages.size()) { + messages.remove(index); + } + } ArrayList dayArr = messagesByDays.get(removed.dateKey); dayArr.remove(obj); if (dayArr.isEmpty()) { @@ -21746,7 +21991,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { if (chatAdapter.isFiltered) { AndroidUtilities.runOnUIThread(() -> checkScrollForLoad(false)); } - if (hashtagSearchEmptyView != null && searchingHashtag != null) { + if (hashtagSearchEmptyView != null && searchingHashtag != null && chatMode != MODE_SEARCH) { hashtagSearchEmptyView.showProgress(false); } } @@ -22431,6 +22676,9 @@ public void didReceivedNotification(int id, int account, final Object... args) { if (jumpToMessageId != 0) { scrollToMessageId(jumpToMessageId, 0, true, 0, true, 0); } else { + if (messagesSearchAdapter.loadedCount <= 0 && messagesSearchListView.getLayoutManager() != null) { + messagesSearchListView.getLayoutManager().scrollToPosition(0); + } messagesSearchAdapter.notifyDataSetChanged(); } if (hashtagSearchEmptyView != null) { @@ -22682,8 +22930,9 @@ private void loadSendAsPeers(boolean animatedUpdate) { private int sponsoredMessagesPostsBetween; private boolean sponsoredMessagesAdded; private Pattern sponsoredUrlPattern; + private MessageObject botSponsoredMessage; private void addSponsoredMessages(boolean animated) { - if (sponsoredMessagesAdded || chatMode != 0 || !ChatObject.isChannel(currentChat) || !forwardEndReached[0] || getUserConfig().isPremium() && getMessagesController().isSponsoredDisabled() || isReport()) { + if (sponsoredMessagesAdded || chatMode != 0 || !ChatObject.isChannel(currentChat) && !UserObject.isBot(currentUser) || !forwardEndReached[0] || getUserConfig().isPremium() && getMessagesController().isSponsoredDisabled() || isReport()) { return; } MessagesController.SponsoredMessagesInfo res = getMessagesController().getSponsoredMessages(dialog_id); @@ -22725,14 +22974,24 @@ private void addSponsoredMessages(boolean animated) { } } sponsoredMessagesAdded = true; - sponsoredMessagesPostsBetween = res.posts_between != null ? res.posts_between : 0; - if (notPushedSponsoredMessages != null) { - notPushedSponsoredMessages.clear(); + if (UserObject.isBot(currentUser)) { + botSponsoredMessage = res == null || res.messages == null || res.messages.isEmpty() ? null : res.messages.get(0); + updateTopPanel(true); + } else { + sponsoredMessagesPostsBetween = res.posts_between != null ? res.posts_between : 0; + if (notPushedSponsoredMessages != null) { + notPushedSponsoredMessages.clear(); + } + processNewMessages(res.messages); } - processNewMessages(res.messages); } public void removeFromSponsored(MessageObject message) { + if (message == botSponsoredMessage) { + botSponsoredMessage = null; + updateTopPanel(true); + return; + } MessagesController.SponsoredMessagesInfo sponsoredMessagesInfo = getMessagesController().getSponsoredMessages(dialog_id); if (sponsoredMessagesInfo != null) { sponsoredMessagesInfo.messages.remove(message); @@ -23069,6 +23328,7 @@ private void rotateMotionBackgroundDrawable() { private ArrayList notPushedSponsoredMessages; private void processNewMessages(ArrayList arr) { + FileLog.d("processNewMessages " + arr.size() + " messages"); long currentUserId = getUserConfig().getClientUserId(); boolean updateChat = false; boolean hasFromMe = false; @@ -23081,6 +23341,7 @@ private void processNewMessages(ArrayList arr) { boolean notifiedSearch = false; LongSparseArray scheduledGroupReplacement = null; for (int a = 0, N = arr.size(); a < N; a++) { + FileLog.d("processNewMessages " + a + " our of " + N); MessageObject messageObject = arr.get(a); if (!isAd) { isAd = messageObject.isSponsored(); @@ -23225,6 +23486,7 @@ private void processNewMessages(ArrayList arr) { int currentMaxDate = Integer.MIN_VALUE; for (int a = 0; a < arr.size(); a++) { + FileLog.d("processNewMessages " + a + " our of " + arr.size()); MessageObject obj = arr.get(a); if (obj.isOut()) { rotateMotionBackgroundDrawable(); @@ -23335,6 +23597,7 @@ private void processNewMessages(ArrayList arr) { MessageObject lastActionSetChatThemeMessageObject = null; int lastAdIndex = -1; for (int a = 0; a < arr.size(); a++) { + FileLog.d("processNewMessages 2. " + a + " out of " + arr.size()); MessageObject obj = arr.get(a); if (obj.scheduled != (chatMode == MODE_SCHEDULED)) { continue; @@ -23363,6 +23626,13 @@ private void processNewMessages(ArrayList arr) { if (removed != null) { int index = messages.indexOf(removed); messages.remove(index); +// MessageObject conversionMsg = conversionMessages.get(removed.getId()); +// if (conversionMsg != null) { +// conversionMessages.remove(removed.getId()); +// if (index >= 0 && index < messages.size()) { +// messages.remove(index); +// } +// } ArrayList dayArr = messagesByDays.get(removed.dateKey); dayArr.remove(removed); if (dayArr.isEmpty()) { @@ -23574,7 +23844,7 @@ private void processNewMessages(ArrayList arr) { if (obj.messageOwner.date == 0x7ffffffe) { dateMsg.message = LocaleController.getString(R.string.MessageScheduledUntilOnline); } else { - dateMsg.message = LocaleController.formatString("MessageScheduledOn", R.string.MessageScheduledOn, LocaleController.formatDateChat(obj.messageOwner.date, true)); + dateMsg.message = LocaleController.formatString(R.string.MessageScheduledOn, LocaleController.formatDateChat(obj.messageOwner.date, true)); } } else { dateMsg.message = LocaleController.formatDateChat(obj.messageOwner.date); @@ -23588,7 +23858,7 @@ private void processNewMessages(ArrayList arr) { calendar.set(Calendar.MILLISECOND, 0); dateMsg.date = (int) (calendar.getTimeInMillis() / 1000); MessageObject dateObj = new MessageObject(currentAccount, dateMsg, false, false); - dateObj.type = 10; + dateObj.type = MessageObject.TYPE_DATE; dateObj.contentType = 1; dateObj.isDateObject = true; dateObj.stableId = getStableIdForDateObject(obj.dateKeyInt); @@ -23791,12 +24061,18 @@ private void processNewMessages(ArrayList arr) { if (savedMessagesTagHint != null && savedMessagesTagHint.shown()) { savedMessagesTagHint.hide(); } + if (videoConversionTimeHint != null && videoConversionTimeHint.shown()) { + videoConversionTimeHint.hide(); + } if (botMessageHint != null && botMessageHint.shown()) { botMessageHint.hide(); } if (factCheckHint != null) { factCheckHint.hide(); } + if (currentUser != null && currentUser.bot) { + updateTopPanel(true); + } } private int getStableIdForDateObject(int date) { @@ -23808,6 +24084,15 @@ private int getStableIdForDateObject(int date) { return id; } + private int getStableIdForConversionObject(int mid) { + int id = conversionObjectsStableIds.get(mid, -1); + if (id == -1) { + id = lastStableId++; + conversionObjectsStableIds.put(mid, id); + } + return id; + } + private void saveScrollPosition() { if (chatListView == null || chatLayoutManager == null || chatLayoutManager.hasPendingScrollPosition()) { return; @@ -23845,6 +24130,9 @@ private int getSponsoredMessagesCount() { } private void processDeletedMessages(ArrayList markAsDeletedMessages, long channelId, boolean sent) { + processDeletedMessages(markAsDeletedMessages, channelId, sent, true); + } + private void processDeletedMessages(ArrayList markAsDeletedMessages, long channelId, boolean sent, boolean thanos) { ArrayList removedIndexes = new ArrayList<>(); ArrayList thanosMessagesIndexes = new ArrayList<>(); final int currentTime = getConnectionsManager().getCurrentTime(); @@ -23968,7 +24256,7 @@ private void processDeletedMessages(ArrayList markAsDeletedMessages, lo messagesSearchAdapter.notifyDataSetChanged(); } removedIndexes.add(chatAdapter.messagesStartRow + index); - if (!sent && !obj.scheduledSent && removed != null && removed.messageOwner != null && removed.messageOwner.send_state == MessageObject.MESSAGE_SEND_STATE_SENT && currentTime - removed.messageOwner.date >= (currentChat != null || currentUser != null && currentUser.bot ? 2 : 0)) { + if (thanos && !sent && !obj.scheduledSent && removed != null && removed.messageOwner != null && removed.messageOwner.send_state == MessageObject.MESSAGE_SEND_STATE_SENT && currentTime - removed.messageOwner.date >= (currentChat != null || currentUser != null && currentUser.bot ? 2 : 0)) { thanosMessagesIndexes.add(chatAdapter.messagesStartRow + index); removed.deletedByThanos = LiteMode.isEnabled(LiteMode.FLAG_CHAT_THANOS); } @@ -23988,6 +24276,20 @@ private void processDeletedMessages(ArrayList markAsDeletedMessages, lo } } messagesDict[loadIndex].remove(mid); + MessageObject conversionMsg = conversionMessages.get(mid); + if (conversionMsg != null) { + conversionMessages.remove(mid); + int dindex = index; + if (chatAdapter != null && chatAdapter.isFiltered) { + dindex = messages.indexOf(obj); + } + if (dindex >= 0 && dindex < messages.size()) { + messages.remove(dindex); + if (chatAdapter != null && !chatAdapter.isFiltered) { + removedIndexes.add(chatAdapter.messagesStartRow + dindex); + } + } + } ArrayList dayArr = messagesByDays.get(obj.dateKey); if (dayArr != null) { dayArr.remove(obj); @@ -24154,6 +24456,9 @@ private void processDeletedMessages(ArrayList markAsDeletedMessages, lo if (savedMessagesTagHint != null && savedMessagesTagHint.shown()) { savedMessagesTagHint.hide(); } + if (videoConversionTimeHint != null && videoConversionTimeHint.shown()) { + videoConversionTimeHint.hide(); + } if (chatMode == MODE_QUICK_REPLIES && messages != null && messages.isEmpty()) { threadMessageId = 0; @@ -24178,7 +24483,7 @@ private void replaceMessageObjects(ArrayList messageObjects, int if (loadIndex == 0 && repliesMessagesDict.indexOfKey(messageObject.getId()) >= 0) { repliesMessagesDict.put(messageObject.getId(), messageObject); } - if (old == null || remove && old.messageOwner.date != messageObject.messageOwner.date) { + if (old == null || remove && old.messageOwner.date != messageObject.messageOwner.date || messageObject.scheduled && chatMode != MODE_SCHEDULED) { continue; } if (remove) { @@ -24301,6 +24606,24 @@ private void replaceMessageObjects(ArrayList messageObjects, int if (chatAdapter != null && !chatAdapter.isFiltered) { chatAdapter.notifyItemRemoved(chatAdapter.messagesStartRow + index); } + MessageObject conversionMsg = conversionMessages.get(old.getId()); + if (conversionMsg != null) { + conversionMessages.remove(old.getId()); + messages.remove(index); + int prevLoadingUpRow = chatAdapter.loadingUpRow; + int prevLoadingDownRow = chatAdapter.loadingDownRow; + if (!chatAdapter.isFiltered) { + chatAdapter.notifyItemRemoved(chatAdapter.messagesStartRow + index); + if (messages.isEmpty()) { + if (prevLoadingUpRow >= 0) { + chatAdapter.notifyItemRemoved(0); + } + if (prevLoadingDownRow >= 0) { + chatAdapter.notifyItemRemoved(0); + } + } + } + } if (index2 >= 0) { dayArr.remove(index2); if (dayArr.isEmpty()) { @@ -24522,6 +24845,67 @@ public void onBecomeFullyVisible() { if (savedMessagesHint != null) { AndroidUtilities.runOnUIThread(this::checkSavedMessagesHint, 600); } + if (convertingToast && !convertingToastShown) { + convertingToastShown = true; + BulletinFactory.of(this) + .createSimpleBulletin(R.raw.convert_video, getString(R.string.VideoConversionTitle), getString(R.string.VideoConversionText)) + .setDuration(Bulletin.DURATION_PROLONG) + .setOnHideListener(this::checkConversionDateTimeToast) + .show(true); + } + } + + private boolean shownConversionDateTimeToast; + private void checkConversionDateTimeToast() { + if (shownConversionDateTimeToast || !isFullyVisible || !chatListView.isAttachedToWindow()) return; + final int[] loc = new int[2]; + ChatMessageCell foundCell = null; + for (int i = chatListView.getChildCount() - 1; i >= 0; --i) { + View child = chatListView.getChildAt(i); + if (child instanceof ChatMessageCell) { + ChatMessageCell cell = (ChatMessageCell) child; + if ( + cell.getMessageObject() != null && cell.getMessageObject().messageOwner != null && cell.getMessageObject().messageOwner.video_processing_pending && + (cell.getCurrentPosition() == null || cell.getMessageObject() != null && (cell.getCurrentPosition().flags & MessageObject.POSITION_FLAG_BOTTOM) != 0 && (cell.getCurrentPosition().flags & (cell.getMessageObject().isOutOwner() ? MessageObject.POSITION_FLAG_LEFT : MessageObject.POSITION_FLAG_RIGHT)) != 0) + ) { + cell.getLocationInWindow(loc); + + final float y = loc[1] + cell.getTimeY(); + if (y >= dp(240) && y <= AndroidUtilities.displaySize.y - dp(25) - AndroidUtilities.navigationBarHeight) { + foundCell = cell; + break; + } + } + } + } + + if (foundCell != null) { + shownConversionDateTimeToast = true; + videoConversionTimeHint = new HintView2(getContext(), HintView2.DIRECTION_BOTTOM) { + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + setTranslationY(-getTop() - dp(120) + videoConversionTimeHintY); + } + } + .setMultilineText(true) + .setTextAlign(Layout.Alignment.ALIGN_CENTER) + .setDuration(3500) + .setHideByTouch(true) + .useScale(true) + .setMaxWidth(150) + .setRounding(8); + videoConversionTimeHint.setText(LocaleController.getString(R.string.VideoConversionTimeInfo)); + contentView.addView(videoConversionTimeHint, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 120, Gravity.TOP | Gravity.FILL_HORIZONTAL, 16, 0, 16, 0)); + foundCell.getLocationInWindow(loc); + videoConversionTimeHintY = loc[1] + foundCell.getTimeY(); + videoConversionTimeHint.setTranslationY(- videoConversionTimeHint.getTop() - dp(120) + videoConversionTimeHintY); + videoConversionTimeHint.setJointPx(0, -dp(16) + loc[0] + foundCell.timeX + foundCell.timeWidth / 2f); + videoConversionTimeHint.show(); + } else { + AndroidUtilities.cancelRunOnUIThread(ChatActivity.this::checkConversionDateTimeToast); + AndroidUtilities.runOnUIThread(ChatActivity.this::checkConversionDateTimeToast, 2000); + } } private boolean checkedSavedMessagesHint; @@ -24818,6 +25202,9 @@ private void checkGroupEmojiPackHint() { if (chatFull == null || chatFull.emojiset == null || chatActivityEnterView == null || getContext() == null) { return; } + if (bottomOverlayChat != null && bottomOverlayChat.getVisibility() == View.VISIBLE) { + return; + } if (MessagesController.getGlobalMainSettings().getBoolean("groupEmojiPackHintShown", false)) { return; } @@ -26516,6 +26903,7 @@ private void updateTopPanel(boolean animated) { !getMessagesController().premiumFeaturesBlocked() && preferences.getInt("dialog_show_translate_count" + did, 5) <= 0 ); boolean showBizBot = currentEncryptedChat == null && getUserConfig().isPremium() && preferences.getLong("dialog_botid" + did, 0) != 0; + boolean showBotAd = currentUser != null && currentUser.bot && messages.size() >= 2 && botSponsoredMessage != null; if (showRestartTopic) { shownRestartTopic = true; } @@ -26529,6 +26917,7 @@ private void updateTopPanel(boolean animated) { return; } } + boolean show2 = showBotAd; if (showBizBot) { createBizBotButton(); @@ -26547,6 +26936,135 @@ private void updateTopPanel(boolean animated) { translateButton.updateText(); } } + if (showBotAd) { + createBotAdView(); + if (botAdView != null) { + markSponsoredAsRead(botSponsoredMessage); + botAdView.set(this, botSponsoredMessage, () -> { + if (botSponsoredMessage == null) return; + RevenueSharingAdsInfoBottomSheet[] sheet = new RevenueSharingAdsInfoBottomSheet[1]; + sheet[0] = RevenueSharingAdsInfoBottomSheet.showAlert(getContext(), ChatActivity.this, true, resourceProvider, o -> { + if (botSponsoredMessage == null) return; + if (botSponsoredMessage.sponsoredInfo != null || botSponsoredMessage.sponsoredAdditionalInfo != null || botSponsoredMessage.sponsoredUrl != null && !botSponsoredMessage.sponsoredUrl.startsWith("https://" + MessagesController.getInstance(currentAccount).linkPrefix)) { + ItemOptions info = o.makeSwipeback(); + + ActionBarMenuSubItem backCell = new ActionBarMenuSubItem(getContext(), true, false, resourceProvider); + backCell.setItemHeight(44); + backCell.setTextAndIcon(getString(R.string.Back), R.drawable.msg_arrow_back); + backCell.getTextView().setPadding(LocaleController.isRTL ? 0 : AndroidUtilities.dp(40), 0, LocaleController.isRTL ? AndroidUtilities.dp(40) : 0, 0); + backCell.setOnClickListener(v1 -> o.closeSwipeback()); + info.addView(backCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + info.addView(new ActionBarPopupWindow.GapView(getContext(), resourceProvider), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 8)); + + ArrayList sections = new ArrayList<>(); + + if (botSponsoredMessage.sponsoredUrl != null && !TextUtils.equals(AndroidUtilities.getHostAuthority(botSponsoredMessage.sponsoredUrl), MessagesController.getInstance(currentAccount).linkPrefix)) { + TextView textView = new TextView(getContext()); + textView.setTextColor(Theme.getColor(Theme.key_chat_messageLinkIn, resourceProvider)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + textView.setPadding(AndroidUtilities.dp(18), AndroidUtilities.dp(10), AndroidUtilities.dp(18), AndroidUtilities.dp(10)); + textView.setMaxWidth(AndroidUtilities.dp(300)); + Uri uri = Uri.parse(botSponsoredMessage.sponsoredUrl); + textView.setText(Browser.replaceHostname(uri, Browser.IDN_toUnicode(uri.getHost()), null)); + textView.setBackground(Theme.createRadSelectorDrawable(getThemedColor(Theme.key_dialogButtonSelector), 0, botSponsoredMessage.sponsoredAdditionalInfo == null ? 6 : 0)); + textView.setOnClickListener(e -> { + if (botSponsoredMessage == null) return; + o.dismiss(); + logSponsoredClicked(botSponsoredMessage, false, true); + Browser.openUrl(getContext(), Uri.parse(botSponsoredMessage.sponsoredUrl), true, false, false, null, null, false, MessagesController.getInstance(currentAccount).sponsoredLinksInappAllow, false); + }); + textView.setOnLongClickListener(e -> { + if (botSponsoredMessage == null) return false; + if (AndroidUtilities.addToClipboard(botSponsoredMessage.sponsoredUrl)) { + BulletinFactory.of(Bulletin.BulletinWindow.make(getContext()), resourceProvider).createCopyLinkBulletin().show(); + } + return true; + }); + sections.add(textView); + } + + if (botSponsoredMessage.sponsoredInfo != null) { + TextView textView = new TextView(getContext()); + textView.setTextColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem, resourceProvider)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + textView.setPadding(AndroidUtilities.dp(18), AndroidUtilities.dp(10), AndroidUtilities.dp(18), AndroidUtilities.dp(10)); + textView.setMaxWidth(AndroidUtilities.dp(300)); + textView.setText(botSponsoredMessage.sponsoredInfo); + textView.setBackground(Theme.createRadSelectorDrawable(getThemedColor(Theme.key_dialogButtonSelector), 0, botSponsoredMessage.sponsoredAdditionalInfo == null ? 6 : 0)); + textView.setOnClickListener(e -> { + if (AndroidUtilities.addToClipboard(botSponsoredMessage.sponsoredInfo)) { + BulletinFactory.of(Bulletin.BulletinWindow.make(getContext()), resourceProvider).createCopyBulletin(LocaleController.getString(R.string.TextCopied)).show(); + } + }); + sections.add(textView); + } + + if (botSponsoredMessage.sponsoredAdditionalInfo != null) { + TextView textView = new TextView(getContext()); + textView.setTextColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem, resourceProvider)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + textView.setPadding(AndroidUtilities.dp(18), AndroidUtilities.dp(10), AndroidUtilities.dp(18), AndroidUtilities.dp(10)); + textView.setMaxWidth(AndroidUtilities.dp(300)); + textView.setText(botSponsoredMessage.sponsoredAdditionalInfo); + textView.setBackground(Theme.createRadSelectorDrawable(getThemedColor(Theme.key_dialogButtonSelector), 0, 6)); + textView.setOnClickListener(e -> { + if (AndroidUtilities.addToClipboard(botSponsoredMessage.sponsoredAdditionalInfo)) { + BulletinFactory.of(Bulletin.BulletinWindow.make(getContext()), resourceProvider).createCopyBulletin(LocaleController.getString(R.string.TextCopied)).show(); + } + }); + sections.add(textView); + } + + for (int i = 0; i < sections.size(); ++i) { + View section = sections.get(i); + if (i > 0) { + FrameLayout separator = new FrameLayout(getContext()); + separator.setBackgroundColor(Theme.getColor(Theme.key_divider, resourceProvider)); + LinearLayout.LayoutParams params = LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 1); + params.height = 1; + info.addView(separator, params); + } + info.addView(section, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } + o.add(R.drawable.msg_channel, getString(R.string.SponsoredMessageSponsorReportable), () -> o.openSwipeback(info)); + o.addGap(); + } + o.add(R.drawable.msg_block2, getString(R.string.ReportAd), () -> { + o.dismiss(); + if (sheet[0] != null) sheet[0].dismiss(); + ReportBottomSheet.openSponsored(ChatActivity.this, botSponsoredMessage, themeDelegate); + }); + o.add(R.drawable.msg_cancel, getString(R.string.HideAd), () -> { + o.dismiss(); + if (sheet[0] != null) sheet[0].dismiss(); + if (getUserConfig().isPremium()) { + botSponsoredMessage = null; + updateTopPanel(true); + BulletinFactory.of(this) + .createAdReportedBulletin(LocaleController.getString(R.string.AdHidden)) + .show(); + getMessagesController().disableAds(true); + } else { + showDialog(new PremiumFeatureBottomSheet(this, PremiumPreviewFragment.PREMIUM_FEATURE_ADS, true)); + } + }); + o.setGravity(Gravity.RIGHT).show(); + }); + }, () -> { + if (getUserConfig().isPremium()) { + botSponsoredMessage = null; + updateTopPanel(true); + BulletinFactory.of(this) + .createAdReportedBulletin(LocaleController.getString(R.string.AdHidden)) + .show(); + getMessagesController().disableAds(true); + } else { + showDialog(new PremiumFeatureBottomSheet(this, PremiumPreviewFragment.PREMIUM_FEATURE_ADS, true)); + } + }); + } + } if ((shownTranslateTopic || shownRestartTopic) && !show) { showReport = showGeo = showShare = showBlock = showAdd = showArchive = showAddMembersToGroup = false; showEmojiStatusReport = null; @@ -26561,6 +27079,9 @@ private void updateTopPanel(boolean animated) { if (translateButton != null) { translateButton.setVisibility(showTranslate ? View.VISIBLE : View.GONE); } + if (botAdView != null) { + botAdView.setVisibility(showBotAd ? View.VISIBLE : View.GONE); + } if (bizBotButton != null) { bizBotButton.setVisibility(showBizBot ? View.VISIBLE : View.GONE); } @@ -26824,6 +27345,16 @@ public void updateDrawState(@NonNull TextPaint ds) { if (topChatPanelView != null) { topChatPanelView.getLayoutParams().height = topChatPanelHeight; } + int topChatPanel2Height = 0; + if (show2) { + topChatPanel2Height += dp(2); + if (showBotAd) { + topChatPanel2Height += botAdView.height(); + } + } + if (topChatPanelView2 != null) { + topChatPanelView2.getLayoutParams().height = topChatPanel2Height; + } if (show) { createTopPanel(); @@ -26919,6 +27450,94 @@ public void onAnimationCancel(Animator animation) { } } } + if (show2) { + createTopPanel2(); + if (topChatPanelView2 == null) { + return; + } + if (topChatPanelView2.getTag() != null) { + topChatPanelView2.setTag(null); + topChatPanelView2.setVisibility(View.VISIBLE); + if (topChatPanelView2Animator != null) { + topChatPanelView2Animator.cancel(); + topChatPanelView2Animator = null; + } + if (animated) { + topChatPanelView2Animator = new AnimatorSet(); + ValueAnimator animator = ValueAnimator.ofFloat(topChatPanelView2Offset, 0); + animator.addUpdateListener(animation -> { + topChatPanelView2Offset = (float) animation.getAnimatedValue(); + invalidateChatListViewTopPadding(); + invalidateMessagesVisiblePart(); + }); + topChatPanelView2Animator.playTogether(animator); + topChatPanelView2Animator.setDuration(200); + topChatPanelView2Animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (topChatPanelView2Animator != null && topChatPanelView2Animator.equals(animation)) { + topChatPanelView2Animator = null; + } + } + + @Override + public void onAnimationCancel(Animator animation) { + if (topChatPanelView2Animator != null && topChatPanelView2Animator.equals(animation)) { + topChatPanelView2Animator = null; + } + } + }); + topChatPanelView2Animator.start(); + } else { + topChatPanelView2Offset = 0; + invalidateChatListViewTopPadding(); + invalidateMessagesVisiblePart(); + } + } + } else if (topChatPanelView2 != null) { + if (topChatPanelView2.getTag() == null) { + topChatPanelView.setTag(1); + + if (topChatPanelView2Animator != null) { + topChatPanelView2Animator.cancel(); + topChatPanelView2Animator = null; + } + if (animated) { + topChatPanelView2Animator = new AnimatorSet(); + ValueAnimator animator = ValueAnimator.ofFloat(topChatPanelView2Offset, -topChatPanel2Height); + animator.addUpdateListener(animation -> { + topChatPanelView2Offset = (float) animation.getAnimatedValue(); + invalidateChatListViewTopPadding(); + invalidateMessagesVisiblePart(); + }); + topChatPanelView2Animator.playTogether(animator); + topChatPanelView2Animator.setDuration(200); + topChatPanelView2Animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (topChatPanelView2Animator != null && topChatPanelView2Animator.equals(animation)) { + topChatPanelView2.setVisibility(View.GONE); + topChatPanelView2Animator = null; + } + invalidateChatListViewTopPadding(); + invalidateMessagesVisiblePart(); + } + + @Override + public void onAnimationCancel(Animator animation) { + if (topChatPanelView2Animator != null && topChatPanelView2Animator.equals(animation)) { + topChatPanelView2Animator = null; + } + } + }); + topChatPanelView2Animator.start(); + } else { + topChatPanelView2Offset = -topChatPanel2Height; + invalidateChatListViewTopPadding(); + invalidateMessagesVisiblePart(); + } + } + } checkListViewPaddings(); } @@ -28553,7 +29172,7 @@ public void setAutoDeleteHistory(int time, int action) { icons.add(R.drawable.msg_calendar2); } MessageObject msg = selectedObjectGroup != null ? selectedObjectGroup.findPrimaryMessageObject() : selectedObject; - if (msg != null && msg.isFactCheckable() && getMessagesController().canEditFactcheck && ChatObject.isChannelAndNotMegaGroup(currentChat)) { + if (msg != null && msg.isFactCheckable() && getMessagesController().canEditFactcheck && ChatObject.isChannelAndNotMegaGroup(currentChat) && chatMode == MODE_DEFAULT) { items.add(LocaleController.getString(msg.getFactCheck() == null ? R.string.AddFactCheck : R.string.EditFactCheck)); options.add(OPTION_FACT_CHECK); icons.add(R.drawable.menu_factcheck); @@ -28717,6 +29336,8 @@ public void setAutoDeleteHistory(int time, int action) { } boolean showMessageSeen = !isReactionsViewAvailable && !isInScheduleMode() && currentChat != null && message.isOutOwner() && message.isSent() && !message.isEditing() && !message.isSending() && !message.isSendError() && !message.isContentUnread() && !message.isUnread() && (ConnectionsManager.getInstance(currentAccount).getCurrentTime() - message.messageOwner.date < getMessagesController().chatReadMarkExpirePeriod) && (ChatObject.isMegagroup(currentChat) || !ChatObject.isChannel(currentChat)) && chatInfo != null && chatInfo.participants_count <= getMessagesController().chatReadMarkSizeThreshold && !(message.messageOwner.action instanceof TLRPC.TL_messageActionChatJoinedByRequest) && (v instanceof ChatMessageCell); boolean showPrivateMessageSeen = !isReactionsViewAvailable && currentChat == null && currentEncryptedChat == null && (currentUser != null && !UserObject.isUserSelf(currentUser) && !UserObject.isReplyUser(currentUser) && !UserObject.isAnonymous(currentUser) && !currentUser.bot && !UserObject.isService(currentUser.id)) && (userInfo == null || !userInfo.read_dates_private) && !isInScheduleMode() && message.isOutOwner() && message.isSent() && !message.isEditing() && !message.isSending() && !message.isSendError() && !message.isContentUnread() && !message.isUnread() && (ConnectionsManager.getInstance(currentAccount).getCurrentTime() - message.messageOwner.date < getMessagesController().pmReadDateExpirePeriod) && !(message.messageOwner.action instanceof TLRPC.TL_messageActionChatJoinedByRequest) && (v instanceof ChatMessageCell); + boolean showPrivateMessageEdit = (currentUser == null || !UserObject.isReplyUser(currentUser) && !UserObject.isAnonymous(currentUser)) && !isInScheduleMode() && message.isEdited() && !(message.messageOwner.action instanceof TLRPC.TL_messageActionChatJoinedByRequest) && (v instanceof ChatMessageCell); + boolean showPrivateMessageFwdOriginal = false && (currentUser == null || !UserObject.isReplyUser(currentUser) && !UserObject.isAnonymous(currentUser)) && !isInScheduleMode() && message.isForwarded() && !(message.messageOwner.action instanceof TLRPC.TL_messageActionChatJoinedByRequest) && (v instanceof ChatMessageCell); boolean showSponsorInfo = selectedObject != null && selectedObject.isSponsored() && (selectedObject.sponsoredInfo != null || selectedObject.sponsoredAdditionalInfo != null || selectedObject.sponsoredUrl != null && !selectedObject.sponsoredUrl.startsWith("https://" + getMessagesController().linkPrefix)); if (chatMode == MODE_SAVED) { showMessageSeen = false; @@ -29066,7 +29687,21 @@ public void onClick(View view) { popupLayout.addView(messageSeenLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 44)); addGap = true; } else if (showPrivateMessageSeen) { - MessagePrivateSeenView messagePrivateSeenView = new MessagePrivateSeenView(getContext(), message, () -> { + MessagePrivateSeenView messagePrivateSeenView = new MessagePrivateSeenView(getContext(), MessagePrivateSeenView.TYPE_SEEN, message, () -> { + closeMenu(true); + }, themeDelegate); + popupLayout.addView(messagePrivateSeenView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 36)); + addGap = true; + } + if (showPrivateMessageEdit) { + MessagePrivateSeenView messagePrivateSeenView = new MessagePrivateSeenView(getContext(), MessagePrivateSeenView.TYPE_EDIT, message, () -> { + closeMenu(true); + }, themeDelegate); + popupLayout.addView(messagePrivateSeenView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 36)); + addGap = true; + } + if (showPrivateMessageFwdOriginal) { + MessagePrivateSeenView messagePrivateSeenView = new MessagePrivateSeenView(getContext(), MessagePrivateSeenView.TYPE_FORWARD, message, () -> { closeMenu(true); }, themeDelegate); popupLayout.addView(messagePrivateSeenView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 36)); @@ -29075,9 +29710,9 @@ public void onClick(View view) { boolean showRateTranscription = selectedObject != null && selectedObject.isVoice() && selectedObject.messageOwner != null && getUserConfig().isPremium() && !TextUtils.isEmpty(selectedObject.messageOwner.voiceTranscription) && selectedObject.messageOwner != null && !selectedObject.messageOwner.voiceTranscriptionRated && selectedObject.messageOwner.voiceTranscriptionId != 0 && selectedObject.messageOwner.voiceTranscriptionOpen; if (!showRateTranscription && message.probablyRingtone() && currentEncryptedChat == null) { - ActionBarMenuSubItem cell = new ActionBarMenuSubItem(getParentActivity(), true, false, themeDelegate); + ActionBarMenuSubItem cell = new ActionBarMenuSubItem(getParentActivity(), !showPrivateMessageSeen && !showPrivateMessageEdit && !showPrivateMessageFwdOriginal, false, themeDelegate); cell.setMinimumWidth(AndroidUtilities.dp(200)); - cell.setTextAndIcon(LocaleController.getString(R.string.SaveForNotifications), R.drawable.msg_tone_add); + cell.setTextAndIcon(getString(R.string.SaveForNotifications), R.drawable.msg_tone_add); popupLayout.addView(cell); cell.setOnClickListener(v1 -> { if (getMediaDataController().saveToRingtones(message.getDocument())) { @@ -29230,7 +29865,7 @@ public void run() { return; } logSponsoredClicked(selectedObject, false, false); - Browser.openUrl(getContext(), Uri.parse(selectedObject.sponsoredUrl), true, false, false, null, null, false, getMessagesController().sponsoredLinksInappAllow); + Browser.openUrl(getContext(), Uri.parse(selectedObject.sponsoredUrl), true, false, false, null, null, false, getMessagesController().sponsoredLinksInappAllow, false); }); textView.setOnLongClickListener(e -> { if (selectedObject == null) { @@ -29485,6 +30120,39 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } } } + if (selectedObject != null && selectedObject.messageOwner != null && selectedObject.messageOwner.video_processing_pending) { + popupLayout.addView(new ActionBarPopupWindow.GapView(contentView.getContext(), themeDelegate), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 8)); + FrameLayout layout = new FrameLayout(getParentActivity()) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + }; + TextView infoText = new TextView(getParentActivity()) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST && getLayout() != null) { + Layout layout = getLayout(); + int width = 0; + for (int i = 0; i < layout.getLineCount(); ++i) { + width = Math.max(width, (int) Math.ceil(layout.getLineWidth(i))); + } + widthMeasureSpec = MeasureSpec.makeMeasureSpec(getPaddingLeft() + width + getPaddingRight(), MeasureSpec.EXACTLY); + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + }; + infoText.setMaxLines(6); + infoText.setGravity(Gravity.LEFT); + infoText.setEllipsize(TextUtils.TruncateAt.END); + infoText.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); + infoText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); + infoText.setMaxWidth(AndroidUtilities.dp(240)); + infoText.setText(getString(R.string.VideoConversionInfo)); + infoText.setPadding(dp(13), dp(8), dp(13), dp(8)); + layout.addView(infoText, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL)); + popupLayout.addView(layout); + } } ChatScrimPopupContainerLayout scrimPopupContainerLayout = new ChatScrimPopupContainerLayout(contentView.getContext()) { @@ -30000,18 +30668,18 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { emptyView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); emptyView.setGravity(Gravity.CENTER); emptyView.setTextColor(getThemedColor(Theme.key_chat_serviceText)); - emptyView.setBackground(Theme.createServiceDrawable(AndroidUtilities.dp(6), emptyView, contentView, getThemedPaint(Theme.key_paint_chatActionBackground))); + emptyView.setBackground(Theme.createServiceDrawable(AndroidUtilities.dp(30), emptyView, contentView, getThemedPaint(Theme.key_paint_chatActionBackground))); emptyView.setTypeface(AndroidUtilities.bold()); - emptyView.setPadding(AndroidUtilities.dp(10), AndroidUtilities.dp(2), AndroidUtilities.dp(10), AndroidUtilities.dp(3)); + emptyView.setPadding(AndroidUtilities.dp(9), AndroidUtilities.dp(2), AndroidUtilities.dp(9), AndroidUtilities.dp(3)); emptyViewContainer.addView(emptyView, new FrameLayout.LayoutParams(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); } } } else { bigEmptyView = new ChatBigEmptyView(getContext(), contentView, ChatBigEmptyView.EMPTY_VIEW_TYPE_SECRET, themeDelegate); if (currentEncryptedChat.admin_id == getUserConfig().getClientUserId()) { - bigEmptyView.setStatusText(LocaleController.formatString("EncryptedPlaceholderTitleOutgoing", R.string.EncryptedPlaceholderTitleOutgoing, UserObject.getFirstName(currentUser))); + bigEmptyView.setStatusText(LocaleController.formatString(R.string.EncryptedPlaceholderTitleOutgoing, UserObject.getFirstName(currentUser))); } else { - bigEmptyView.setStatusText(LocaleController.formatString("EncryptedPlaceholderTitleIncoming", R.string.EncryptedPlaceholderTitleIncoming, UserObject.getFirstName(currentUser))); + bigEmptyView.setStatusText(LocaleController.formatString(R.string.EncryptedPlaceholderTitleIncoming, UserObject.getFirstName(currentUser))); } emptyViewContainer.addView(bigEmptyView, new FrameLayout.LayoutParams(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); } @@ -30782,7 +31450,7 @@ private void processSelectedOption(int option) { break; } case OPTION_ADD_TO_STICKERS_OR_MASKS: { - StickersAlert alert = new StickersAlert(getParentActivity(), this, selectedObject.getInputStickerSet(), null, bottomOverlayChat.getVisibility() != View.VISIBLE && (currentChat == null || ChatObject.canSendStickers(currentChat)) ? chatActivityEnterView : null, themeDelegate); + StickersAlert alert = new StickersAlert(getParentActivity(), this, selectedObject.getInputStickerSet(), null, bottomOverlayChat.getVisibility() != View.VISIBLE && (currentChat == null || ChatObject.canSendStickers(currentChat)) ? chatActivityEnterView : null, themeDelegate, false); alert.setCalcMandatoryInsets(isKeyboardVisible()); preserveDim = true; alert.setDimBehind(false); @@ -31169,38 +31837,54 @@ private void processSelectedOption(int option) { break; } case OPTION_SEND_NOW: { - if (!checkSlowMode(chatActivityEnterView.getSendButton())) { - if (getMediaController().isPlayingMessage(selectedObject)) { - getMediaController().cleanupPlayer(true, true); - } - TLRPC.TL_messages_sendScheduledMessages req = new TLRPC.TL_messages_sendScheduledMessages(); - req.peer = getMessagesController().getInputPeer(dialog_id); - if (selectedObjectGroup != null) { - for (int a = 0; a < selectedObjectGroup.messages.size(); a++) { - req.id.add(selectedObjectGroup.messages.get(a).getId()); - } - } else { - req.id.add(selectedObject.getId()); + closeMenu(!preserveDim); + Runnable send = () -> { + if (!checkSlowMode(chatActivityEnterView.getSendButton())) { + if (getMediaController().isPlayingMessage(selectedObject)) { + getMediaController().cleanupPlayer(true, true); + } + TLRPC.TL_messages_sendScheduledMessages req = new TLRPC.TL_messages_sendScheduledMessages(); + req.peer = getMessagesController().getInputPeer(dialog_id); + if (selectedObjectGroup != null) { + for (int a = 0; a < selectedObjectGroup.messages.size(); a++) { + req.id.add(selectedObjectGroup.messages.get(a).getId()); + } + } else { + req.id.add(selectedObject.getId()); + } + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { + if (error == null) { + TLRPC.Updates updates = (TLRPC.Updates) response; + getMessagesController().processUpdates(updates, false); + AndroidUtilities.runOnUIThread(() -> NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.messagesDeleted, req.id, getUserConfig().getClientUserId() == dialog_id ? 0 : -dialog_id, true, true)); + } else if (error.text != null) { + AndroidUtilities.runOnUIThread(() -> { + if (error.text.startsWith("SLOWMODE_WAIT_")) { + AlertsCreator.showSimpleToast(ChatActivity.this, LocaleController.getString(R.string.SlowmodeSendError)); + } else if (error.text.equals("CHAT_SEND_MEDIA_FORBIDDEN")) { + AlertsCreator.showSimpleToast(ChatActivity.this, LocaleController.getString(R.string.AttachMediaRestrictedForever)); + } else { + AlertsCreator.showSimpleToast(ChatActivity.this, error.text); + } + }); + } + }); } - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { - if (error == null) { - TLRPC.Updates updates = (TLRPC.Updates) response; - getMessagesController().processUpdates(updates, false); - AndroidUtilities.runOnUIThread(() -> NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.messagesDeleted, req.id, getUserConfig().getClientUserId() == dialog_id ? 0 : -dialog_id, true, true)); - } else if (error.text != null) { - AndroidUtilities.runOnUIThread(() -> { - if (error.text.startsWith("SLOWMODE_WAIT_")) { - AlertsCreator.showSimpleToast(ChatActivity.this, LocaleController.getString(R.string.SlowmodeSendError)); - } else if (error.text.equals("CHAT_SEND_MEDIA_FORBIDDEN")) { - AlertsCreator.showSimpleToast(ChatActivity.this, LocaleController.getString(R.string.AttachMediaRestrictedForever)); - } else { - AlertsCreator.showSimpleToast(ChatActivity.this, error.text); - } - }); - } - }); - break; + }; + if (selectedObject != null && selectedObject.messageOwner != null && selectedObject.messageOwner.video_processing_pending) { + scheduleNowDialog = new AlertDialog.Builder(getContext(), getResourceProvider()) + .setTitle(LocaleController.getString(R.string.VideoConversionNowTitle)) + .setMessage(LocaleController.getString(R.string.VideoConversionNowText)) + .setPositiveButton(LocaleController.getString(R.string.VideoConversionNowSend), (di, w) -> { + send.run(); + }) + .setNegativeButton(LocaleController.getString(R.string.Cancel), null) + .show(); + return; + } else { + send.run(); } + break; } case OPTION_EDIT_SCHEDULE_TIME: { MessageObject message = selectedObject; @@ -31222,11 +31906,10 @@ private void processSelectedOption(int option) { break; } case OPTION_ABOUT_REVENUE_SHARING_ADS: { - RevenueSharingAdsInfoBottomSheet.showAlert(contentView.getContext(), ChatActivity.this, resourceProvider); + RevenueSharingAdsInfoBottomSheet.showAlert(contentView.getContext(),ChatActivity.this, false, resourceProvider); break; } case OPTION_REPORT_AD: { - MessageObject message = selectedObject; ReportBottomSheet.openSponsored(ChatActivity.this, selectedObject, themeDelegate); break; } @@ -31896,6 +32579,10 @@ public void openSearchWithText(String text) { } public void openHashtagSearch(String hashtag) { + openHashtagSearch(hashtag, false); + } + + public void openHashtagSearch(String hashtag, boolean forcePublic) { if (hashtag.isEmpty() || (!hashtag.startsWith("#") && !hashtag.startsWith("$"))) { return; } @@ -31915,6 +32602,7 @@ public void openHashtagSearch(String hashtag) { } searchingHashtag = hashtag; searchingQuery = searchingHashtag; + boolean channelHashtags = hashtag.contains("@"); checkHashtagStories(true); if (!actionBar.isSearchFieldVisible()) { AndroidUtilities.updateViewVisibilityAnimated(avatarContainer, false, 0.95f, true); @@ -31946,27 +32634,28 @@ public void openHashtagSearch(String hashtag) { if (searchUserButton != null) { searchUserButton.setVisibility(View.GONE); } - if (ChatObject.isChannelAndNotMegaGroup(currentChat) && ChatObject.isPublic(currentChat) && searchingHashtag != null) { + if (channelHashtags || forcePublic || ChatObject.isChannelAndNotMegaGroup(currentChat) && ChatObject.isPublic(currentChat) && searchingHashtag != null) { defaultSearchPage = 2; } else { defaultSearchPage = 0; } openSearchKeyboard = false; + if (searchType == SEARCH_CHANNEL_POSTS) { + HashtagSearchController.getInstance(currentAccount).clearSearchResults(SEARCH_CHANNEL_POSTS); + } else { + HashtagSearchController.getInstance(currentAccount).clearSearchResults(); + } + if (searchViewPager != null) { + searchViewPager.clearViews(); + } if (searchItem != null) { preventReopenSearchWithText = true; searchItem.openSearch(false); preventReopenSearchWithText = false; } if (searchItem != null) { - Spannable spannable; - if (hashtag.startsWith("$")) { - spannable = new SpannableString("$"); - } else { - spannable = new SpannableString("#"); - } - spannable.setSpan(new ForegroundColorSpan(getThemedColor(Theme.key_windowBackgroundWhiteGrayText)), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - searchItem.setSearchFieldCaption(spannable); - searchItem.setSearchFieldText(hashtag.substring(1), false); + searchItem.setSearchFieldCaption(null); + searchItem.setSearchFieldText(hashtag, false); searchItem.setSearchFieldHint(LocaleController.getString(R.string.SearchHashtagsHint)); } getMediaDataController().searchMessagesInChat(searchingQuery, dialog_id, mergeDialogId, classGuid, 0, threadMessageId, false, searchingUserMessages, searchingChatMessages, false, searchingReaction); @@ -31974,15 +32663,16 @@ public void openHashtagSearch(String hashtag) { hashtagSearchEmptyView.showProgress(true); showMessagesSearchListView(true); if (hashtagSearchTabs != null) { - hashtagSearchTabs.show(true); - messagesSearchListContainer.setPadding(0, hashtagSearchTabs.getLayoutParams().height, 0, 0); + hashtagSearchTabs.show(!channelHashtags); + messagesSearchListContainer.setPadding(0, 0, 0, getHashtagTabsHeight()); updateSearchListEmptyView(); } - HashtagSearchController.getInstance(currentAccount).clearSearchResults(); + if ((channelHashtags || forcePublic) && searchingHashtag != null && hashtagSearchTabs != null && hashtagSearchTabs.tabs.getCurrentPosition() != defaultSearchPage) { + hashtagSearchTabs.tabs.scrollToTab(defaultSearchPage, defaultSearchPage); + } HashtagSearchController.getInstance(currentAccount).putToHistory(searchingHashtag); hashtagHistoryView.update(); View view = searchViewPager.getCurrentView(); - HashtagSearchController.getInstance(currentAccount).clearSearchResults(); if (view instanceof ChatActivityContainer) { ((ChatActivityContainer) view).chatActivity.updateSearchingHashtag(searchingHashtag); } @@ -32042,6 +32732,59 @@ private void openSearchWithUser(TLRPC.User user) { searchUserMessages(user, null); updatePinnedMessageView(true); } + private void openSearchWithChat(TLRPC.Chat chat) { + boolean delay = false; + if (savedMessagesHint != null && savedMessagesHint.shown()) { + savedMessagesHint.hide(); + delay = true; + } + if (savedMessagesSearchHint != null && savedMessagesSearchHint.shown()) { + savedMessagesSearchHint.hide(); + delay = true; + } + if (delay) { + AndroidUtilities.runOnUIThread(() -> openSearchWithChat(chat), 200); + return; + } + if (!actionBar.isSearchFieldVisible()) { + AndroidUtilities.updateViewVisibilityAnimated(avatarContainer, false, 0.95f, true); + if (headerItem != null) { + headerItem.setVisibility(View.GONE); + } + if (attachItem != null) { + attachItem.setVisibility(View.GONE); + } + if (editTextItem != null) { + editTextItem.setVisibility(View.GONE); + } + if (searchItem != null) { + searchItem.setVisibility(View.VISIBLE); + } + if (searchIconItem != null && showSearchAsIcon) { + searchIconItem.setVisibility(View.GONE); + } + if (audioCallIconItem != null && showAudioCallAsIcon) { + audioCallIconItem.setVisibility(View.GONE); + } + searchItemVisible = true; + updateSearchButtons(0, 0, -1); + updateBottomOverlay(); + + if (searchCalendarButton != null) { + searchCalendarButton.setVisibility(View.GONE); + } + if (searchUserButton != null) { + searchUserButton.setVisibility(View.GONE); + } + } + if (searchItem != null) { + preventReopenSearchWithText = true; + searchItem.openSearch(openSearchKeyboard = false); + preventReopenSearchWithText = false; + } + searchUserMessages(null, chat); + updatePinnedMessageView(true); + } private void updateSearchListEmptyView() { if (searchingHashtag != null) { @@ -32560,7 +33303,8 @@ private void openMessageInOriginalDialog(MessageObject messageObject) { args.putInt("message_id", messageObject.getRealId()); args.putBoolean("need_remove_previous_same_chat_activity", false); if (getMessagesController().checkCanOpenChat(args, ChatActivity.this)) { - presentFragment(DialogsActivity.highlightFoundQuote(new ChatActivity(args), messageObject)); +// presentFragment(DialogsActivity.highlightFoundQuote(new ChatActivity(args), messageObject)); + presentFragment(new ChatActivity(args)); } } @@ -32887,6 +33631,8 @@ private void openClickableLink(CharacterStyle url, String str, boolean longPress if (chatMode != MODE_SEARCH) { finishFragment(); } + } else if (str.contains("@")) { + presentFragment(new HashtagActivity(str, resourceProvider)); } else { openHashtagSearch(str); } @@ -33057,9 +33803,9 @@ public void logSponsoredClicked(MessageObject messageObject, boolean media, bool if (messageObject == null || !messageObject.isSponsored()) { return; } - TLRPC.TL_channels_clickSponsoredMessage req = new TLRPC.TL_channels_clickSponsoredMessage(); + TLRPC.TL_messages_clickSponsoredMessage req = new TLRPC.TL_messages_clickSponsoredMessage(); req.random_id = messageObject.sponsoredId; - req.channel = getMessagesController().getInputChannel(-getDialogId()); + req.peer = getMessagesController().getInputPeer(getDialogId()); req.media = media; req.fullscreen = fullscreen; getConnectionsManager().sendRequest(req, null); @@ -33272,16 +34018,17 @@ private void updateMessageListAccessibilityVisibility() { } private void markSponsoredAsRead(MessageObject object) { + if (object == null) { + return; + } if (!object.isSponsored() || object.viewsReloaded) { return; } object.viewsReloaded = true; - TLRPC.TL_channels_viewSponsoredMessage req = new TLRPC.TL_channels_viewSponsoredMessage(); - req.channel = MessagesController.getInputChannel(currentChat); + TLRPC.TL_messages_viewSponsoredMessage req = new TLRPC.TL_messages_viewSponsoredMessage(); + req.peer = getMessagesController().getInputPeer(getDialogId()); req.random_id = object.sponsoredId; - getConnectionsManager().sendRequest(req, (response, error) -> { - - }); + getConnectionsManager().sendRequest(req, null); getMessagesController().markSponsoredAsRead(dialog_id, object); } @@ -34840,7 +35587,11 @@ public void onSearchCollapse() { } searchItemVisible = false; getMediaDataController().clearFoundMessageObjects(); - HashtagSearchController.getInstance(currentAccount).clearSearchResults(); + if (searchType == SEARCH_CHANNEL_POSTS) { + HashtagSearchController.getInstance(currentAccount).clearSearchResults(SEARCH_CHANNEL_POSTS); + } else { + HashtagSearchController.getInstance(currentAccount).clearSearchResults(); + } if (messagesSearchAdapter != null) { messagesSearchAdapter.notifyDataSetChanged(); } @@ -34944,28 +35695,59 @@ public void onSearchPressed(EditText editText) { searchWas = true; updateSearchButtons(0, 0, -1); + boolean hashtags = false; + boolean channelHashtags = false; searchingQuery = editText.getText().toString(); - if (searchingHashtag != null) { - if (TextUtils.isEmpty(searchingQuery)) { + if (!TextUtils.isEmpty(searchingQuery) && (searchingQuery.startsWith("$") || searchingQuery.startsWith("#"))) { + hashtags = true; + if (searchingQuery.contains("@")) { + presentFragment(new HashtagActivity(searchingQuery, resourceProvider)); return; } - if (searchingHashtag.startsWith("$")) { - searchingHashtag = "$" + searchingQuery; - } else { - searchingHashtag = "#" + searchingQuery; + if (searchingHashtag == null) { + searchingHashtag = searchingQuery; + whiteActionBar = true; + ValueAnimator valueAnimator = ValueAnimator.ofFloat(searchAnimationProgress, 1f); + valueAnimator.addUpdateListener(valueAnimator1 -> setSearchAnimationProgress((float) valueAnimator1.getAnimatedValue())); + valueAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + valueAnimator.setDuration(320); + valueAnimator.start(); + if (actionBarSearchTags != null) { + actionBarSearchTags.show(!isInsideContainer && actionBarSearchTags.hasFilters() && searchingHashtag == null); + } } - searchingQuery = searchingHashtag; + searchingHashtag = searchingQuery; checkHashtagStories(true); HashtagSearchController.getInstance(currentAccount).putToHistory(searchingHashtag); hashtagHistoryView.update(); View view = searchViewPager.getCurrentView(); - HashtagSearchController.getInstance(currentAccount).clearSearchResults(); + if (searchType == SEARCH_CHANNEL_POSTS) { + HashtagSearchController.getInstance(currentAccount).clearSearchResults(SEARCH_CHANNEL_POSTS); + } else { + HashtagSearchController.getInstance(currentAccount).clearSearchResults(); + } if (view instanceof ChatActivityContainer) { ((ChatActivityContainer) view).chatActivity.updateSearchingHashtag(searchingHashtag); } updateSearchListEmptyView(); hashtagSearchEmptyView.showProgress(true); showMessagesSearchListView(true); + } else { + searchingHashtag = null; + if (hashtagSearchTabs != null) { + hashtagSearchTabs.show(false); + messagesSearchListContainer.setPadding(0, 0, 0, getHashtagTabsHeight()); + updateSearchListEmptyView(); + } + if (hashtagSearchTabs != null && hashtagSearchTabs.tabs.getCurrentPosition() != 0) { + hashtagSearchTabs.tabs.scrollToTab(0, 0); + } + } + if (hashtagSearchTabs != null) { + hashtagSearchTabs.show(hashtags && !channelHashtags); + } + if (channelHashtags && searchingHashtag != null && hashtagSearchTabs != null && hashtagSearchTabs.tabs.getCurrentPosition() != defaultSearchPage) { + hashtagSearchTabs.tabs.scrollToTab(defaultSearchPage, defaultSearchPage); } getMediaDataController().searchMessagesInChat(searchingQuery, dialog_id, mergeDialogId, classGuid, 0, threadMessageId, searchingUserMessages, searchingChatMessages, searchingReaction); @@ -35563,11 +36345,57 @@ public boolean didLongPressUserAvatar(ChatMessageCell cell, TLRPC.User user, flo } }); return true; + } else { + ItemOptions.makeOptions(ChatActivity.this, cell) + .add(R.drawable.msg_openprofile, getString(R.string.OpenProfile), () -> { + openProfile(user); + }) + .add(R.drawable.msg_discussion, getString(R.string.SendMessage), () -> { + openDialog(cell, user); + }) + .addIf(enableMention, R.drawable.msg_mention, getString(R.string.Mention), () -> { + appendMention(user); + }) + .addIf(enableSearchMessages, R.drawable.msg_search, getString(R.string.AvatarPreviewSearchMessages), () -> { + openSearchWithUser(user); + }) + .setDrawScrim(false) + .setGravity(Gravity.LEFT) + .forceBottom(true) + .translate(0, -dp(48)) + .show(); + return true; } } return false; } + private void appendMention(TLRPC.Chat chat) { + if (chatActivityEnterView != null) { + SpannableStringBuilder sb; + final CharSequence text = chatActivityEnterView.getFieldText(); + if (text != null) { + sb = new SpannableStringBuilder(text); + if (text.charAt(text.length() - 1) != ' ') { + sb.append(" "); + } + } else { + sb = new SpannableStringBuilder(); + } + if (sb.length() > 0 && sb.charAt(sb.length() - 1) != ' ') { + sb.append(' '); + } + String username = ChatObject.getPublicUsername(chat); + if (username != null) { + sb.append("@").append(username).append(" "); + } else { + return; + } + chatActivityEnterView.setFieldText(sb); + AndroidUtilities.runOnUIThread(() -> chatActivityEnterView.openKeyboard(), 200); + } + } + private void appendMention(TLRPC.User user) { if (chatActivityEnterView != null) { SpannableStringBuilder sb; @@ -35600,10 +36428,20 @@ private void appendMention(TLRPC.User user) { @Override public boolean didLongPressChannelAvatar(ChatMessageCell cell, TLRPC.Chat chat, int postId, float touchX, float touchY) { if (isAvatarPreviewerEnabled()) { - AvatarPreviewer.MenuItem[] menuItems = {AvatarPreviewer.MenuItem.OPEN_PROFILE}; - if (currentChat == null || currentChat.id != chat.id || isThreadChat()) { - menuItems = Arrays.copyOf(menuItems, 2); - menuItems[1] = chat.broadcast ? AvatarPreviewer.MenuItem.OPEN_CHANNEL : AvatarPreviewer.MenuItem.OPEN_GROUP; + final boolean enableMention = !TextUtils.isEmpty(ChatObject.getPublicUsername(chat)) && currentChat != null && (bottomOverlayChat == null || bottomOverlayChat.getVisibility() != View.VISIBLE) && (bottomOverlay == null || bottomOverlay.getVisibility() != View.VISIBLE); + final boolean enableSearchMessages = currentChat != null && (threadMessageId == 0 || isTopic) && (!ChatObject.isChannel(currentChat) || currentChat.megagroup); + final boolean openChannel = currentChat == null || currentChat.id != chat.id || isThreadChat(); + final AvatarPreviewer.MenuItem[] menuItems = new AvatarPreviewer.MenuItem[1 + (openChannel ? 1 : 0) + (enableMention ? 1 : 0) + (enableSearchMessages ? 1 : 0)]; + int a = 0; + menuItems[a++] = AvatarPreviewer.MenuItem.OPEN_PROFILE; + if (openChannel) { + menuItems[a++] = chat.broadcast ? AvatarPreviewer.MenuItem.OPEN_CHANNEL : AvatarPreviewer.MenuItem.OPEN_GROUP; + } + if (enableMention) { + menuItems[a++] = AvatarPreviewer.MenuItem.MENTION; + } + if (enableSearchMessages) { + menuItems[a++] = AvatarPreviewer.MenuItem.SEARCH_MESSAGES; } final TLRPC.ChatFull chatFull = getMessagesController().getChatFull(chat.id); final AvatarPreviewer.Data data; @@ -35618,6 +36456,12 @@ public boolean didLongPressChannelAvatar(ChatMessageCell cell, TLRPC.Chat chat, case OPEN_PROFILE: openProfile(chat); break; + case MENTION: + appendMention(chat); + break; + case SEARCH_MESSAGES: + openSearchWithChat(chat); + break; case OPEN_GROUP: case OPEN_CHANNEL: openChat(cell, chat, 0, false); @@ -35625,6 +36469,26 @@ public boolean didLongPressChannelAvatar(ChatMessageCell cell, TLRPC.Chat chat, } }); return true; + } else { + ItemOptions.makeOptions(ChatActivity.this, cell) + .add(R.drawable.msg_openprofile, getString(R.string.OpenProfile), () -> { + openProfile(chat); + }) + .addIf(openChannel, chat.broadcast ? R.drawable.msg_channel : R.drawable.msg_discussion, getString(chat.broadcast ? R.string.OpenChannel2 : R.string.OpenGroup2), () -> { + openChat(cell, chat, 0, false); + }) + .addIf(enableMention, R.drawable.msg_mention, getString(R.string.Mention), () -> { + appendMention(chat); + }) + .addIf(enableSearchMessages, R.drawable.msg_search, getString(R.string.AvatarPreviewSearchMessages), () -> { + openSearchWithChat(chat); + }) + .setDrawScrim(false) + .setGravity(Gravity.LEFT) + .forceBottom(true) + .translate(0, -dp(48)) + .show(); + return true; } } return false; @@ -35813,6 +36677,9 @@ public void didPressReaction(ChatMessageCell cell, TLRPC.ReactionCount reaction, if (savedMessagesTagHint != null && savedMessagesTagHint.shown()) { savedMessagesTagHint.hide(); } + if (videoConversionTimeHint != null && videoConversionTimeHint.shown()) { + videoConversionTimeHint.hide(); + } if (getUserConfig().getClientUserId() == getDialogId() && cell.areTags() && !getUserConfig().isPremium()) { if (longpress) return; new PremiumFeatureBottomSheet(ChatActivity.this, PremiumPreviewFragment.PREMIUM_FEATURE_SAVED_TAGS, true).show(); @@ -36184,6 +37051,11 @@ public boolean canPerformActions() { return actionBar != null && !actionBar.isActionModeShowed() && !isReport() && !inPreviewMode; } + @Override + public boolean canPerformReply() { + return !isReport() && !inPreviewMode; + } + @Override public void didPressUrl(ChatMessageCell cell, final CharacterStyle url, boolean longPress) { didPressMessageUrl(url, longPress, cell.getMessageObject(), cell); @@ -36377,12 +37249,23 @@ public void end(boolean replaced) { if (!safe && !Browser.isInternalUri(uri, null)) { AlertsCreator.showOpenUrlAlert(ChatActivity.this, url, true, true, true, !safe, progressDialogCurrent, themeDelegate); } else { - Browser.openUrl(getContext(), uri, true, true, false, progressDialogCurrent, null, false, true); + Browser.openUrl(getContext(), uri, true, true, false, progressDialogCurrent, null, false, true, false); } } @Override - public void didPressReplyMessage(ChatMessageCell cell, int id) { + public void didPressReplyMessage(ChatMessageCell cell, int id, float x, float y, boolean longpress) { + if (!canPerformActions()) { + if (!longpress) { + if (actionBar.isActionModeShowed() || isReport()) { + if (textSelectionHelper.isSelected(cell.getMessageObject())) { + return; + } + processRowSelect(cell, !cell.isInsideBackground(x, y), x, y); + } + return; + } + } if (UserObject.isReplyUser(currentUser)) { didPressSideButton(cell); return; @@ -36739,7 +37622,7 @@ public void didPressImage(ChatMessageCell cell, float x, float y) { alert.setCalcMandatoryInsets(isKeyboardVisible()); showDialog(alert); } else if (message.getInputStickerSet() != null) { - StickersAlert alert = new StickersAlert(getParentActivity(), ChatActivity.this, message.getInputStickerSet(), null, bottomOverlayChat.getVisibility() != View.VISIBLE && (currentChat == null || ChatObject.canSendStickers(currentChat)) ? chatActivityEnterView : null, themeDelegate); + StickersAlert alert = new StickersAlert(getParentActivity(), ChatActivity.this, message.getInputStickerSet(), null, bottomOverlayChat.getVisibility() != View.VISIBLE && (currentChat == null || ChatObject.canSendStickers(currentChat)) ? chatActivityEnterView : null, themeDelegate, false); alert.setCalcMandatoryInsets(isKeyboardVisible()); showDialog(alert); } else if (message.isVideo() || message.type == MessageObject.TYPE_PHOTO || message.type == MessageObject.TYPE_TEXT && !message.isWebpageDocument() || message.isGif()) { @@ -36766,7 +37649,7 @@ public void end(boolean replaced) { } } }; - Browser.openUrl(getContext(), Uri.parse(message.sponsoredUrl), true, false, false, progressDialogCurrent, null, false, getMessagesController().sponsoredLinksInappAllow); + Browser.openUrl(getContext(), Uri.parse(message.sponsoredUrl), true, false, false, progressDialogCurrent, null, false, getMessagesController().sponsoredLinksInappAllow, false); } return; } else if (message.isVideo()) { @@ -37008,7 +37891,7 @@ public void end(boolean replaced) { alert.setCalcMandatoryInsets(isKeyboardVisible()); showDialog(alert); } else { - StickersAlert alert = new StickersAlert(getParentActivity(), ChatActivity.this, inputStickerSet, null, chatActivityEnterView, themeDelegate); + StickersAlert alert = new StickersAlert(getParentActivity(), ChatActivity.this, inputStickerSet, null, chatActivityEnterView, themeDelegate, false); alert.setCalcMandatoryInsets(isKeyboardVisible()); showDialog(alert); } @@ -37020,7 +37903,7 @@ public void end(boolean replaced) { return; } } - Browser.openUrl(getParentActivity(), Uri.parse(webPage.url), true, true, false, progressDialogCurrent, null, false, true); + Browser.openUrl(getParentActivity(), Uri.parse(webPage.url), true, true, false, progressDialogCurrent, null, false, true, false); } else { if (messageObject.isSponsored()) { logSponsoredClicked(messageObject, false, false); @@ -37044,7 +37927,7 @@ public void end(boolean replaced) { } } }; - Browser.openUrl(getContext(), Uri.parse(messageObject.sponsoredUrl), true, false, false, progressDialogCurrent, null, false, getMessagesController().sponsoredLinksInappAllow); + Browser.openUrl(getContext(), Uri.parse(messageObject.sponsoredUrl), true, false, false, progressDialogCurrent, null, false, getMessagesController().sponsoredLinksInappAllow, false); } } else { TLRPC.WebPage webPage = messageObject.getStoryMentionWebpage(); @@ -37088,7 +37971,7 @@ public void end(boolean replaced) { } } }; - Browser.openUrl(getParentActivity(), Uri.parse(webPage.url), true, true, false, progressDialogCurrent, null, false, true); + Browser.openUrl(getParentActivity(), Uri.parse(webPage.url), true, true, false, progressDialogCurrent, null, false, true, false); } } } @@ -37224,7 +38107,7 @@ public void didPressAboutRevenueSharingAds() { if (contentView == null || getParentActivity() == null) { return; } - RevenueSharingAdsInfoBottomSheet.showAlert(contentView.getContext(), ChatActivity.this, resourceProvider); + RevenueSharingAdsInfoBottomSheet.showAlert(contentView.getContext(), ChatActivity.this, false, resourceProvider); } @Override @@ -37631,6 +38514,9 @@ public ArrayList getThemeDescriptions() { if (topChatPanelView != null) { topChatPanelView.backgroundColor = getThemedColor(Theme.key_chat_topPanelBackground); } + if (topChatPanelView2 != null) { + topChatPanelView2.backgroundColor = getThemedColor(Theme.key_chat_topPanelBackground); + } if (contentView != null) { contentView.invalidateBlurredViews(); contentView.invalidateBackground(); @@ -38034,6 +38920,7 @@ public ArrayList getThemeDescriptions() { themeDescriptions.add(new ThemeDescription(pinnedListButton, ThemeDescription.FLAG_IMAGECOLOR, null, null, null, null, Theme.key_chat_topPanelClose)); themeDescriptions.add(new ThemeDescription(closeReportSpam, ThemeDescription.FLAG_IMAGECOLOR, null, null, null, null, Theme.key_chat_topPanelClose)); themeDescriptions.add(new ThemeDescription(topChatPanelView, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_chat_topPanelBackground)); + themeDescriptions.add(new ThemeDescription(topChatPanelView2, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_chat_topPanelBackground)); themeDescriptions.add(new ThemeDescription(alertView, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_chat_topPanelBackground)); themeDescriptions.add(new ThemeDescription(pinnedMessageView, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_chat_topPanelBackground)); themeDescriptions.add(new ThemeDescription(addToContactsButton, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_chat_addContact)); @@ -38271,6 +39158,9 @@ public AnimatorSet onCustomTransitionAnimation(boolean isOpen, Runnable callback if (previousChat.topChatPanelView != null) { previousChat.topChatPanelView.setAlpha(1f - progress); } + if (previousChat.topChatPanelView2 != null) { + previousChat.topChatPanelView2.setAlpha(1f - progress); + } }); updateChatListViewTopPadding(); @@ -38318,6 +39208,9 @@ public void onAnimationEnd(Animator animation) { if (previousChat.topChatPanelView != null) { previousChat.topChatPanelView.setAlpha(1f); } + if (previousChat.topChatPanelView2 != null) { + previousChat.topChatPanelView2.setAlpha(1f); + } } }); fragmentTransition.setDuration(300); @@ -39424,21 +40317,24 @@ public ThanosEffect getChatThanosEffect() { if (!LiteMode.isEnabled(LiteMode.FLAG_CHAT_THANOS) || !ThanosEffect.supports()) { return null; } - if (chatListThanosEffect == null) { + if (chatListThanosEffect == null || chatListThanosEffect.destroyed) { if (getContext() == null || !ThanosEffect.supports() || chatListView == null || contentView == null) { return null; } + if (chatListThanosEffect != null) { + AndroidUtilities.removeFromParent(chatListThanosEffect); + } final ThanosEffect[] thisThanosEffect = new ThanosEffect[1]; final ThanosEffect thanosEffect = new ThanosEffect(getContext(), () -> { if (removingFromParent || thisThanosEffect[0] == null) { return; } ThanosEffect effect = thisThanosEffect[0]; - AndroidUtilities.removeFromParent(effect); thisThanosEffect[0] = null; if (chatListThanosEffect == effect) { chatListThanosEffect = null; } + AndroidUtilities.removeFromParent(effect); }); thisThanosEffect[0] = chatListThanosEffect = thanosEffect; contentView.addView(thanosEffect, 1 + contentView.indexOfChild(chatListView), LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); @@ -39590,7 +40486,7 @@ public void didLongPressLink(ChatMessageCell cell, MessageObject messageObject, }); } - options.add(R.drawable.msg_copy, getString(isMail ? R.string.CopyMail : R.string.CopyLink), () -> { + options.add(R.drawable.msg_copy, getString(isHashtag ? R.string.CopyHashtag : isMail ? R.string.CopyMail : R.string.CopyLink), () -> { if (str.startsWith("video?") && messageObject != null && !messageObject.scheduled) { MessageObject messageObject1 = messageObject; boolean isMedia = messageObject.isVideo() || messageObject.isRoundVideo() || messageObject.isVoice() || messageObject.isMusic(); @@ -40033,10 +40929,12 @@ private void updateViews() { views.add(searchDownButton); views.add(searchContainer); views.add(topChatPanelView); + views.add(topChatPanelView2); views.add(chatListView); views.add(messagesSearchListContainer); views.add(mentionContainer); views.add(floatingDateView); + views.add(chatActivityEnterView); views.removeAll(Collections.singleton(null)); } @@ -40107,4 +41005,10 @@ private void gotChatInfo() { public boolean allowFinishFragmentInsteadOfRemoveFromStack() { return !inPreviewMode; } + + public int getHashtagTabsHeight() { + ChatActivity chatActivity = parentChatActivity != null ? parentChatActivity : this; + if (chatActivity.hashtagSearchTabs == null) return 0; + return chatActivity.hashtagSearchTabs.getCurrentHeight(); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java index 6356fc61d2e..e8d5d132465 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java @@ -10,6 +10,7 @@ import static org.telegram.messenger.AndroidUtilities.dp; import static org.telegram.messenger.LocaleController.getString; +import static org.telegram.ui.ChannelMonetizationLayout.replaceTON; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -67,7 +68,6 @@ import org.telegram.tgnet.tl.TL_stories; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; -import org.telegram.ui.ActionBar.ActionBarPopupWindow; import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.BottomSheet; @@ -77,7 +77,6 @@ import org.telegram.ui.Cells.RadioButtonCell; import org.telegram.ui.Cells.ShadowSectionCell; import org.telegram.ui.Cells.TextCell; -import org.telegram.ui.Cells.TextCheckCell; import org.telegram.ui.Cells.TextDetailCell; import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.Cells.TextSettingsCell; @@ -101,10 +100,14 @@ import org.telegram.ui.Components.UndoView; import org.telegram.ui.Stars.BotStarsActivity; import org.telegram.ui.Stars.BotStarsController; +import org.telegram.ui.Stars.StarsIntroActivity; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.concurrent.CountDownLatch; public class ChatEditActivity extends BaseFragment implements ImageUpdater.ImageUpdaterDelegate, NotificationCenter.NotificationCenterDelegate { @@ -145,6 +148,7 @@ public class ChatEditActivity extends BaseFragment implements ImageUpdater.Image private TextInfoPrivacyCell stickersInfoCell; private LinearLayout infoContainer; + private LinearLayout balanceContainer; private TextCell membersCell; private TextCell memberRequestsCell; private TextCell inviteLinksCell; @@ -160,7 +164,8 @@ public class ChatEditActivity extends BaseFragment implements ImageUpdater.Image private ShadowSectionCell deleteInfoCell; private TextCell publicLinkCell; - private TextCell balanceCell; + private TextCell tonBalanceCell; + private TextCell starsBalanceCell; private TextCell editIntroCell; private TextCell editCommandsCell; private TextCell changeBotSettingsCell; @@ -1166,32 +1171,8 @@ public void afterTextChanged(Editable editable) { presentFragment(new ChangeUsernameActivity(args)); }); - if (currentUser.bot && currentUser.bot_can_edit) { - balanceCell = new TextCell(context); - balanceCell.setBackground(Theme.getSelectorDrawable(false)); - balanceCell.setPrioritizeTitleOverValue(true); - infoContainer.addView(balanceCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - BotStarsController c = BotStarsController.getInstance(currentAccount); - balanceCell.setOnClickListener(v -> { - if (!c.isBalanceAvailable(userId)) - return; - presentFragment(new BotStarsActivity(userId)); - }); - if (!c.isBalanceAvailable(userId)) { - SpannableStringBuilder loadingStr = new SpannableStringBuilder("x"); - loadingStr.setSpan(new LoadingSpan(balanceCell.valueTextView, dp(30)), 0, loadingStr.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - balanceCell.setTextAndValueAndIcon(getString(R.string.BotBalance), loadingStr, R.drawable.menu_premium_main, false); - } else { - balanceCell.setTextAndValueAndIcon(getString(R.string.BotBalance), LocaleController.formatNumber(c.getBalance(userId), ' '), R.drawable.menu_premium_main, false); - } - balanceCell.setVisibility(c.hasStars(userId) ? View.VISIBLE : View.GONE); - } updatePublicLinksCount(); - ActionBarPopupWindow.GapView gap = new ActionBarPopupWindow.GapView(context, getResourceProvider(), Theme.key_windowBackgroundGray); - gap.setTag(R.id.fit_width_tag, 1); - infoContainer.addView(gap, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 8)); - editIntroCell = new TextCell(context); editIntroCell.setBackground(Theme.getSelectorDrawable(false)); editIntroCell.setTextAndIcon(getString(R.string.BotEditIntro), R.drawable.msg_log, true); @@ -1221,7 +1202,7 @@ public void afterTextChanged(Editable editable) { infoSectionCell = new ShadowSectionCell(context); linearLayout1.addView(infoSectionCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); } - } else { + } else if (currentUser != null) { botInfoCell = new TextInfoPrivacyCell(context); String str = getString(R.string.BotManageInfo); SpannableString span = SpannableString.valueOf(str); @@ -1243,9 +1224,78 @@ public void updateDrawState(TextPaint ds) { botInfoCell.setBackground(Theme.getThemedDrawableByKey(getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); botInfoCell.setText(span); linearLayout1.addView(botInfoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + if (currentUser.bot && currentUser.bot_can_edit) { + + balanceContainer = new LinearLayout(context); + balanceContainer.setOrientation(LinearLayout.VERTICAL); + balanceContainer.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + linearLayout1.addView(balanceContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + HeaderCell headerCell = new HeaderCell(context); + headerCell.setText(getString(R.string.BotBalance)); + balanceContainer.addView(headerCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + tonBalanceCell = new TextCell(context); + tonBalanceCell.setBackground(Theme.getSelectorDrawable(false)); + tonBalanceCell.setPrioritizeTitleOverValue(true); + balanceContainer.addView(tonBalanceCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + BotStarsController c = BotStarsController.getInstance(currentAccount); + tonBalanceCell.setOnClickListener(v -> { + if (!c.isStarsBalanceAvailable(userId)) + return; + presentFragment(new BotStarsActivity(BotStarsActivity.TYPE_TON, userId)); + }); + if (!c.isTONBalanceAvailable(userId)) { + SpannableStringBuilder loadingStr = new SpannableStringBuilder("x"); + loadingStr.setSpan(new LoadingSpan(tonBalanceCell.valueTextView, dp(30)), 0, loadingStr.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + tonBalanceCell.setTextAndValueAndIcon(getString(R.string.BotBalanceTON), loadingStr, R.drawable.msg_ton, false); + } else { + long ton_balance = c.getTONBalance(userId); + SpannableStringBuilder ssb = new SpannableStringBuilder(); + if (ton_balance > 0) { + if (ton_balance / 1_000_000_000.0 > 1000.0) { + ssb.append("TON ").append(AndroidUtilities.formatWholeNumber((int) (ton_balance / 1_000_000_000.0), 0)); + } else { + DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US); + symbols.setDecimalSeparator('.'); + DecimalFormat formatterTON = new DecimalFormat("#.##", symbols); + formatterTON.setMinimumFractionDigits(2); + formatterTON.setMaximumFractionDigits(3); + formatterTON.setGroupingUsed(false); + ssb.append("TON ").append(formatterTON.format(ton_balance / 1_000_000_000.0)); + } + } + tonBalanceCell.setTextAndValueAndIcon(getString(R.string.BotBalanceTON), ssb, R.drawable.msg_ton, true); + } + tonBalanceCell.setVisibility(c.botHasTON(userId) ? View.VISIBLE : View.GONE); + + starsBalanceCell = new TextCell(context); + starsBalanceCell.setBackground(Theme.getSelectorDrawable(false)); + starsBalanceCell.setPrioritizeTitleOverValue(true); + balanceContainer.addView(starsBalanceCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + starsBalanceCell.setOnClickListener(v -> { + if (!c.isStarsBalanceAvailable(userId)) + return; + presentFragment(new BotStarsActivity(BotStarsActivity.TYPE_STARS, userId)); + }); + if (!c.isStarsBalanceAvailable(userId)) { + SpannableStringBuilder loadingStr = new SpannableStringBuilder("x"); + loadingStr.setSpan(new LoadingSpan(starsBalanceCell.valueTextView, dp(30)), 0, loadingStr.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + starsBalanceCell.setTextAndValueAndIcon(getString(R.string.BotBalanceStars), loadingStr, R.drawable.menu_premium_main, false); + } else { + starsBalanceCell.setTextAndValueAndIcon(getString(R.string.BotBalanceStars), c.getBotStarsBalance(userId)<=0?"":StarsIntroActivity.replaceStarsWithPlain("XTR" + LocaleController.formatNumber(c.getBotStarsBalance(userId), ' '), .85f), R.drawable.menu_premium_main, false); + } + starsBalanceCell.setVisibility(c.botHasStars(userId) ? View.VISIBLE : View.GONE); + + TextInfoPrivacyCell gap = new TextInfoPrivacyCell(context, getResourceProvider()); + gap.setBackground(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + gap.setTag(R.id.fit_width_tag, 1); + linearLayout1.addView(gap, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 8)); + } } - if (currentUser == null && currentChat.creator) { + if (currentChat != null && currentChat.creator) { deleteContainer = new FrameLayout(context); deleteContainer.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); linearLayout1.addView(deleteContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); @@ -1261,15 +1311,17 @@ public void updateDrawState(TextPaint ds) { deleteCell.setText(getString("DeleteAndExitButton", R.string.DeleteAndExitButton), false); } deleteContainer.addView(deleteCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - deleteCell.setOnClickListener(v -> AlertsCreator.createClearOrDeleteDialogAlert(ChatEditActivity.this, false, true, false, currentChat, null, false, true, false, (param) -> { - if (AndroidUtilities.isTablet()) { - getNotificationCenter().postNotificationName(NotificationCenter.closeChats, -chatId); - } else { - getNotificationCenter().postNotificationName(NotificationCenter.closeChats); - } - finishFragment(); - getNotificationCenter().postNotificationName(NotificationCenter.needDeleteDialog, -currentChat.id, null, currentChat, param); - }, null)); + deleteCell.setOnClickListener(v -> { + AlertsCreator.createClearOrDeleteDialogAlert(ChatEditActivity.this, false, true, false, currentChat, null, false, true, false, (param) -> { + if (AndroidUtilities.isTablet()) { + getNotificationCenter().postNotificationName(NotificationCenter.closeChats, -chatId); + } else { + getNotificationCenter().postNotificationName(NotificationCenter.closeChats); + } + finishFragment(); + getNotificationCenter().postNotificationName(NotificationCenter.needDeleteDialog, -currentChat.id, null, currentChat, param); + }, null); + }); deleteInfoCell = new ShadowSectionCell(context); deleteInfoCell.setBackground(Theme.getThemedDrawableByKey(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); @@ -1312,9 +1364,9 @@ private void updatePublicLinksCount() { } } - publicLinkCell.setTextAndValueAndIcon(getString(R.string.BotPublicLinks), LocaleController.formatString(R.string.BotPublicLinksCount, usernamesActive, currentUser.usernames.size()), R.drawable.msg_link2, balanceCell != null && balanceCell.getVisibility() == View.VISIBLE); + publicLinkCell.setTextAndValueAndIcon(getString(R.string.BotPublicLinks), LocaleController.formatString(R.string.BotPublicLinksCount, usernamesActive, currentUser.usernames.size()), R.drawable.msg_link2, true); } else { - publicLinkCell.setTextAndValueAndIcon(getString(R.string.BotPublicLink), "t.me/" + currentUser.username, R.drawable.msg_link2, balanceCell != null && balanceCell.getVisibility() == View.VISIBLE); + publicLinkCell.setTextAndValueAndIcon(getString(R.string.BotPublicLink), "t.me/" + currentUser.username, R.drawable.msg_link2, true); } } @@ -1420,12 +1472,35 @@ public void didReceivedNotification(int id, int account, Object... args) { } } else if (id == NotificationCenter.botStarsUpdated) { if ((long) args[0] == userId) { - if (balanceCell != null) { + if (starsBalanceCell != null) { BotStarsController c = BotStarsController.getInstance(currentAccount); - balanceCell.setVisibility(c.hasStars(userId) ? View.VISIBLE : View.GONE); - balanceCell.setValue(LocaleController.formatNumber(c.getBalance(userId), ' '), true); + starsBalanceCell.setVisibility(c.botHasStars(userId) ? View.VISIBLE : View.GONE); + starsBalanceCell.setValue(StarsIntroActivity.replaceStarsWithPlain("XTR" + LocaleController.formatNumber(c.getBotStarsBalance(userId), ' '), .85f), true); + if (publicLinkCell != null) { + publicLinkCell.setNeedDivider(c.botHasStars(userId) || c.botHasTON(userId)); + } + } + if (tonBalanceCell != null) { + BotStarsController c = BotStarsController.getInstance(currentAccount); + tonBalanceCell.setVisibility(c.botHasTON(userId) ? View.VISIBLE : View.GONE); + long ton_balance = c.getTONBalance(userId); + SpannableStringBuilder ssb = new SpannableStringBuilder(); + if (ton_balance > 0) { + if (ton_balance / 1_000_000_000.0 > 1000.0) { + ssb.append("TON ").append(AndroidUtilities.formatWholeNumber((int) (ton_balance / 1_000_000_000.0), 0)); + } else { + DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US); + symbols.setDecimalSeparator('.'); + DecimalFormat formatterTON = new DecimalFormat("#.##", symbols); + formatterTON.setMinimumFractionDigits(2); + formatterTON.setMaximumFractionDigits(3); + formatterTON.setGroupingUsed(false); + ssb.append("TON ").append(formatterTON.format(ton_balance / 1_000_000_000.0)); + } + } + tonBalanceCell.setValue(ssb, true); if (publicLinkCell != null) { - publicLinkCell.setNeedDivider(c.hasStars(userId)); + publicLinkCell.setNeedDivider(c.botHasStars(userId) || c.botHasTON(userId)); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChooseDownloadQualityLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/ChooseDownloadQualityLayout.java index 323ac976f69..0019ac46a60 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChooseDownloadQualityLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChooseDownloadQualityLayout.java @@ -4,19 +4,27 @@ import static org.telegram.messenger.LocaleController.getString; import android.content.Context; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.Spanned; import android.text.TextUtils; import android.view.Gravity; import android.widget.FrameLayout; import android.widget.LinearLayout; +import androidx.core.graphics.ColorUtils; + import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.R; import org.telegram.messenger.SharedConfig; +import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.ActionBarMenuSubItem; import org.telegram.ui.ActionBar.ActionBarPopupWindow; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.ColoredImageSpan; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.PopupSwipeBackLayout; import org.telegram.ui.Components.VideoPlayer; @@ -72,19 +80,26 @@ public boolean update(MessageObject messageObject) { buttonsLayout.removeAllViews(); for (int i = 0; i < qualities.size(); ++i) { final VideoPlayer.Quality q = qualities.get(i); - String title = "", subtitle = ""; - String str = q.toString(); - if (str.contains("\n")) { - title = str.substring(0, str.indexOf("\n")); - subtitle = str.substring(str.indexOf("\n") + 1); + final VideoPlayer.VideoUri uri = q.getDownloadUri(); + String title = LocaleController.formatString(R.string.QualitySaveIn, q.p()) + (q.original ? " (" + LocaleController.getString(R.string.QualitySource) + ")" : ""); + SpannableStringBuilder subtitle = new SpannableStringBuilder(); + if (uri.isCached()) { + subtitle.append(AndroidUtilities.formatFileSize(uri.document.size)); + subtitle.append(LocaleController.getString(R.string.QualityCached)); } else { - title = str; + final SpannableString s = new SpannableString("s "); + final ColoredImageSpan span = new ColoredImageSpan(R.drawable.msg_mini_arrow_mediabold); + span.rotate(90.0f); + span.translate(0, dp(1)); + span.spaceScaleX = .85f; + s.setSpan(span, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + subtitle.append(s); + subtitle.append(AndroidUtilities.formatFileSize(uri.document.size)); } ActionBarMenuSubItem item = ActionBarMenuItem.addItem(buttonsLayout, 0, title, false, null); - if (!TextUtils.isEmpty(subtitle)) { - item.setSubtext(subtitle); - } + item.setSubtext(subtitle); item.setColors(0xfffafafa, 0xfffafafa); + item.subtextView.setPadding(0, 0, 0, 0); item.setOnClickListener((view) -> { callback.onQualitySelected(messageObject, q); }); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChooseQualityLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/ChooseQualityLayout.java index 41f82c4e634..ee46f6b3f65 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChooseQualityLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChooseQualityLayout.java @@ -7,8 +7,11 @@ import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; +import android.graphics.Xfermode; import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.view.Gravity; @@ -112,32 +115,46 @@ public interface Callback { public static class QualityIcon extends Drawable { private final Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint bgLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); private final Drawable base; private final RectF rect = new RectF(); - public final AnimatedTextView.AnimatedTextDrawable text = new AnimatedTextView.AnimatedTextDrawable(); + public final AnimatedTextView.AnimatedTextDrawable topText = new AnimatedTextView.AnimatedTextDrawable(); + public final AnimatedTextView.AnimatedTextDrawable bottomText = new AnimatedTextView.AnimatedTextDrawable(); + + private final Callback callback = new Callback() { + @Override + public void invalidateDrawable(@NonNull Drawable who) { + QualityIcon.this.invalidateSelf(); + } + @Override + public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) { + QualityIcon.this.scheduleSelf(what, when); + } + @Override + public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) { + QualityIcon.this.unscheduleSelf(what); + } + }; public QualityIcon(Context context) { base = context.getResources().getDrawable(R.drawable.msg_settings).mutate(); - text.setTypeface(AndroidUtilities.getTypeface("fonts/num.otf")); - text.setTextColor(0xFFFFFFFF); - text.setTextSize(dp(8)); - text.setCallback(new Callback() { - @Override - public void invalidateDrawable(@NonNull Drawable who) { - QualityIcon.this.invalidateSelf(); - } - @Override - public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) { - QualityIcon.this.scheduleSelf(what, when); - } - @Override - public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) { - QualityIcon.this.unscheduleSelf(what); - } - }); - text.setGravity(Gravity.CENTER); - text.setOverrideFullWidth(AndroidUtilities.displaySize.x); + bgLinePaint.setColor(0xFFFFFFFF); + bgLinePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + + topText.setTypeface(AndroidUtilities.getTypeface("fonts/num.otf")); + topText.setTextColor(0xFF000000); + topText.setTextSize(dp(7)); + topText.setCallback(callback); + topText.setGravity(Gravity.CENTER); + topText.setOverrideFullWidth(AndroidUtilities.displaySize.x); + + bottomText.setTypeface(AndroidUtilities.getTypeface("fonts/num.otf")); + bottomText.setTextColor(0xFF000000); + bottomText.setTextSize(dp(7)); + bottomText.setCallback(callback); + bottomText.setGravity(Gravity.CENTER); + bottomText.setOverrideFullWidth(AndroidUtilities.displaySize.x); } private float rotation; @@ -148,23 +165,59 @@ public void setRotation(float rotation) { @Override public void draw(@NonNull Canvas canvas) { + final float top_w = dp(5) * topText.isNotEmpty() + topText.getCurrentWidth(); + final float bottom_w = dp(5) * bottomText.isNotEmpty() + bottomText.getCurrentWidth(); + final Rect bounds = getBounds(); + if (top_w > 0 || bottom_w > 0) + canvas.saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom, 0xFF, Canvas.ALL_SAVE_FLAG); - base.setBounds(bounds); + AndroidUtilities.rectTmp2.set(dp(6), dp(6), dp(6) + (int) bounds.width() - dp(12), dp(6) + (int) bounds.height() - dp(12)); + base.setBounds(AndroidUtilities.rectTmp2); canvas.save(); canvas.rotate(rotation * -180, bounds.centerX(), bounds.centerY()); base.draw(canvas); canvas.restore(); - bgPaint.setColor(Theme.getColor(Theme.key_featuredStickers_addButton)); - final float right = bounds.left + bounds.width() * .97f; - final float cy = bounds.top + bounds.height() * .75f; - final float h = dp(11); - final float w = dp(5) * text.isNotEmpty() + text.getCurrentWidth(); - rect.set(right - w, cy - h / 2f, right, cy + h / 2f); - canvas.drawRoundRect(rect, dp(3), dp(3), bgPaint); - text.setBounds(rect); - text.draw(canvas); + bgPaint.setColor(0xFFFFFFFF); + final float right = bounds.left + bounds.width() * .98f; + final float cy_top = bounds.top + bounds.height() * .18f; + final float cy_bottom = bounds.top + bounds.height() * .78f; + final float h = dp(10); + + if (top_w > 0) { + rect.set(right - top_w, cy_top - h / 2f, right, cy_top + h / 2f); + canvas.drawRoundRect(rect, dp(3), dp(3), bgLinePaint); + } + if (bottom_w > 0) { + rect.set(right - bottom_w, cy_bottom - h / 2f, right, cy_bottom + h / 2f); + canvas.drawRoundRect(rect, dp(3), dp(3), bgLinePaint); + } + + if (top_w > 0 || bottom_w > 0) + canvas.restore(); + + if (top_w > 0) { + bgPaint.setAlpha((int) (0xFF * topText.isNotEmpty())); + topText.setAlpha((int) (0xFF * topText.isNotEmpty())); + rect.set(right - top_w, cy_top - h / 2f, right, cy_top + h / 2f); + rect.inset(dp(1), dp(1)); + canvas.drawRoundRect(rect, dp(3), dp(3), bgPaint); + rect.inset(-dp(1), -dp(1)); + topText.setBounds(rect); + topText.draw(canvas); + } + + if (bottom_w > 0) { + bgPaint.setAlpha((int) (0xFF * bottomText.isNotEmpty())); + bottomText.setAlpha((int) (0xFF * bottomText.isNotEmpty())); + rect.set(right - bottom_w, cy_bottom - h / 2f, right, cy_bottom + h / 2f); + rect.inset(dp(1), dp(1)); + canvas.drawRoundRect(rect, dp(3), dp(3), bgPaint); + rect.inset(-dp(1), -dp(1)); + bottomText.setBounds(rect); + bottomText.draw(canvas); + } } @Override @@ -184,12 +237,12 @@ public int getOpacity() { @Override public int getIntrinsicWidth() { - return base.getIntrinsicWidth(); + return base.getIntrinsicWidth() + dp(12); } @Override public int getIntrinsicHeight() { - return base.getIntrinsicHeight(); + return base.getIntrinsicHeight() + dp(12); } } 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 d89d5c009ce..1c7744c2995 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java @@ -95,7 +95,6 @@ import org.telegram.tgnet.SerializedData; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; -import org.telegram.tgnet.tl.TL_stories; import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.ActionBarPopupWindow; import org.telegram.ui.ActionBar.AlertDialog; @@ -1216,7 +1215,7 @@ public static void showOpenUrlAlert(BaseFragment fragment, String url, boolean p long inlineReturn = (fragment instanceof ChatActivity) ? ((ChatActivity) fragment).getInlineReturn() : 0; final String scheme = url == null ? null : Uri.parse(url).getScheme(); if (Browser.isInternalUrl(url, null) || !ask || "mailto".equalsIgnoreCase(scheme)) { - Browser.openUrl(fragment.getParentActivity(), Uri.parse(url), inlineReturn == 0, tryTelegraph, forceNotInternalForApps && checkInternalBotApp(url), progress, null, false, true); + Browser.openUrl(fragment.getParentActivity(), Uri.parse(url), inlineReturn == 0, tryTelegraph, forceNotInternalForApps && checkInternalBotApp(url), progress, null, false, true, false); } else { String urlFinal; if (punycode) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFileDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFileDrawable.java index a81116148ad..d41457a002f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFileDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFileDrawable.java @@ -56,21 +56,21 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable, private final boolean USE_BITMAP_SHADER = Build.VERSION.SDK_INT < 29; private boolean PRERENDER_FRAME = true; - private static native long createDecoder(String src, int[] params, int account, long streamFileSize, Object readCallback, boolean preview); + public static native long createDecoder(String src, int[] params, int account, long streamFileSize, Object readCallback, boolean preview); - private static native void destroyDecoder(long ptr); + public static native void destroyDecoder(long ptr); - private static native void stopDecoder(long ptr); + public static native void stopDecoder(long ptr); - private static native int getVideoFrame(long ptr, Bitmap bitmap, int[] params, int stride, boolean preview, float startTimeSeconds, float endTimeSeconds, boolean loop); + public static native int getVideoFrame(long ptr, Bitmap bitmap, int[] params, int stride, boolean preview, float startTimeSeconds, float endTimeSeconds, boolean loop); - private static native void seekToMs(long ptr, long ms, boolean precise); + public static native void seekToMs(long ptr, long ms, int[] params, boolean precise); - private static native int getFrameAtTime(long ptr, long ms, Bitmap bitmap, int[] data, int stride); + public static native int getFrameAtTime(long ptr, long ms, Bitmap bitmap, int[] data, int stride); - private static native void prepareToSeek(long ptr); + public static native void prepareToSeek(long ptr); - private static native void getVideoInfo(int sdkVersion, String src, int[] params); + public static native void getVideoInfo(int sdkVersion, String src, int[] params); public final static int PARAM_NUM_SUPPORTED_VIDEO_CODEC = 0; public final static int PARAM_NUM_WIDTH = 1; @@ -183,6 +183,10 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable, public void run() { chekDestroyDecoder(); loadFrameTask = null; + if (pendingSeekToUI >= 0 && pendingSeekTo == -1) { + pendingSeekToUI = -1; + invalidateAfter = 0; + } scheduleNextGetFrame(); invalidateInternal(); } @@ -268,7 +272,16 @@ public void run() { } loadFrameTask = null; - if (!PRERENDER_FRAME) { + if (pendingSeekToUI >= 0) { + nextRenderingBitmap = backgroundBitmap; + nextRenderingBitmapTime = backgroundBitmapTime; + nextRenderingBitmap2 = null; + nextRenderingBitmapTime2 = 0; + for (int i = 0; i < backgroundShader.length; i++) { + nextRenderingShader[i] = backgroundShader[i]; + nextRenderingShader2[i] = null; + } + } else if (!PRERENDER_FRAME) { nextRenderingBitmap = backgroundBitmap; nextRenderingBitmapTime = backgroundBitmapTime; for (int i = 0; i < backgroundShader.length; i++) { @@ -429,7 +442,7 @@ public void run() { if (stream != null) { stream.reset(); } - seekToMs(nativePtr, seekTo, true); + seekToMs(nativePtr, seekTo, metaData,true); } if (backgroundBitmap != null) { lastFrameDecodeTime = System.currentTimeMillis(); @@ -549,7 +562,7 @@ public Bitmap getFrameAtTime(long ms, boolean precise) { stream.reset(); } if (!precise) { - seekToMs(nativePtr, ms, precise); + seekToMs(nativePtr, ms, metaData, precise); } Bitmap backgroundBitmap = Bitmap.createBitmap(metaData[0], metaData[1], Bitmap.Config.ARGB_8888); int result; @@ -1260,7 +1273,7 @@ public void updateCurrentFrame(long now, boolean b) { if (isRunning) { if (renderingBitmap == null && nextRenderingBitmap == null) { scheduleNextGetFrame(); - } else if (nextRenderingBitmap != null && (renderingBitmap == null || (Math.abs(now - lastFrameTime) >= invalidateAfter && !skipFrameUpdate))) { + } else if (nextRenderingBitmap != null && (renderingBitmap == null || (Math.abs(now - lastFrameTime) >= invalidateAfter && !skipFrameUpdate && pendingSeekToUI < 0))) { unusedBitmaps.add(renderingBitmap); renderingBitmap = nextRenderingBitmap; renderingBitmapTime = nextRenderingBitmapTime; 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 90e98008233..a6306b4cc0d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedTextView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedTextView.java @@ -121,6 +121,7 @@ public void setEmojiCacheType(int cacheType) { private long animateDelay = 0; private long animateDuration = 320; private TimeInterpolator animateInterpolator = CubicBezierInterpolator.EASE_OUT_QUINT; + private float animateWave = -1; private float moveAmplitude = .3f; private float scaleAmplitude = 0; @@ -131,10 +132,15 @@ public void setEmojiCacheType(int cacheType) { private boolean splitByWords; private boolean preserveIndex; private boolean startFromEnd; + private boolean enforceByLetter; public void setHacks(boolean splitByWords, boolean preserveIndex, boolean startFromEnd) { + setHacks(splitByWords, preserveIndex, startFromEnd, false); + } + public void setHacks(boolean splitByWords, boolean preserveIndex, boolean startFromEnd, boolean enforceByLetter) { this.splitByWords = splitByWords; this.preserveIndex = preserveIndex; this.startFromEnd = startFromEnd; + this.enforceByLetter = enforceByLetter; } private Runnable onAnimationFinishListener; @@ -160,9 +166,14 @@ public AnimatedTextDrawable() { } public AnimatedTextDrawable(boolean splitByWords, boolean preserveIndex, boolean startFromEnd) { + this(splitByWords, preserveIndex, startFromEnd, false); + } + + public AnimatedTextDrawable(boolean splitByWords, boolean preserveIndex, boolean startFromEnd, boolean enforceByLetter) { this.splitByWords = splitByWords; this.preserveIndex = preserveIndex; this.startFromEnd = startFromEnd; + this.enforceByLetter = enforceByLetter; } public void setAllowCancel(boolean allowCancel) { @@ -185,6 +196,8 @@ private void applyAlphaInternal(float t) { } } + public boolean centerY = true; + @Override public void draw(@NonNull Canvas canvas) { if (ellipsizeByGradient) { @@ -199,7 +212,7 @@ public void draw(@NonNull Canvas canvas) { if (currentParts != null && oldParts != null && t != 1) { float width = lerp(oldWidth, currentWidth, t); float height = lerp(oldHeight, currentHeight, t); - canvas.translate(0, (fullHeight - height) / 2f); + if (centerY) canvas.translate(0, (fullHeight - height) / 2f); for (int i = 0; i < currentParts.length; ++i) { Part current = currentParts[i]; int j = current.toOppositeIndex; @@ -207,6 +220,10 @@ public void draw(@NonNull Canvas canvas) { if (isRTL && !ignoreRTL) { x = currentWidth - (x + current.width); } + float localT = t; + if (animateWave > 0) { + localT = AndroidUtilities.cascade(t, i, currentParts.length, animateWave); + } if (j >= 0) { Part old = oldParts[j]; float oldX = old.offset; @@ -217,8 +234,8 @@ public void draw(@NonNull Canvas canvas) { applyAlphaInternal(1f); } else { x -= current.left; - y = -textPaint.getTextSize() * moveAmplitude * (1f - t) * (moveDown ? 1f : -1f); - applyAlphaInternal(t); + y = -textPaint.getTextSize() * moveAmplitude * (1f - localT) * (moveDown ? 1f : -1f); + applyAlphaInternal(localT); } canvas.save(); float lwidth = j >= 0 ? width : currentWidth; @@ -245,9 +262,13 @@ public void draw(@NonNull Canvas canvas) { if (j >= 0) { continue; } + float localT = t; + if (animateWave > 0) { + localT = AndroidUtilities.cascade(t, i, oldParts.length, animateWave); + } float x = old.offset; - float y = textPaint.getTextSize() * moveAmplitude * t * (moveDown ? 1f : -1f); - applyAlphaInternal(1f - t); + float y = textPaint.getTextSize() * moveAmplitude * localT * (moveDown ? 1f : -1f); + applyAlphaInternal(1f - localT); canvas.save(); if (isRTL && !ignoreRTL) { x = oldWidth - (x + old.width); @@ -267,11 +288,11 @@ public void draw(@NonNull Canvas canvas) { final float s = lerp(1f, 1f - scaleAmplitude, t); canvas.scale(s, s, old.width / 2f, old.layout.getHeight() / 2f); } - old.draw(canvas, 1f - t); + old.draw(canvas, 1f - localT); canvas.restore(); } } else { - canvas.translate(0, (fullHeight - currentHeight) / 2f); + if (centerY) canvas.translate(0, (fullHeight - currentHeight) / 2f); if (currentParts != null) { applyAlphaInternal(1f); for (int i = 0; i < currentParts.length; ++i) { @@ -764,11 +785,20 @@ else if (partEquals(oldText, newText, i - 1, j - 1)) } } + private void part(RegionCallback onPart, CharSequence text, int start, int end) { + if (enforceByLetter && text.length() > 1) { + for (int i = 0; i < text.length(); ++i) { + onPart.run(text.subSequence(i, i + 1), start + i, start + i + 1); + } + return; + } + onPart.run(text, start, end); + } private void diff(final CharSequence oldText, final CharSequence newText, RegionCallback onEqualPart, RegionCallback onNewPart, RegionCallback onOldPart) { if (updateAll) { - onOldPart.run(oldText, 0, oldText.length()); - onNewPart.run(newText, 0, newText.length()); + part(onOldPart, oldText, 0, oldText.length()); + part(onNewPart, newText, 0, newText.length()); return; } if (preserveIndex) { @@ -796,10 +826,10 @@ private void diff(final CharSequence oldText, final CharSequence newText, Region int a = newText.length() - minLength; int b = oldText.length() - minLength; if (a > 0) { - onNewPart.run(newText.subSequence(0, a), 0, a); + part(onNewPart, newText.subSequence(0, a), 0, a); } if (b > 0) { - onOldPart.run(oldText.subSequence(0, b), 0, b); + part(onOldPart, oldText.subSequence(0, b), 0, b); } for (int i = indexes.size() - 1; i >= 0; --i) { int count = indexes.get(i); @@ -810,8 +840,8 @@ private void diff(final CharSequence oldText, final CharSequence newText, Region onEqualPart.run(oldText.subSequence(b, b + count), b, b + count); } } else { - onNewPart.run(newText.subSequence(a, a + count), a, a + count); - onOldPart.run(oldText.subSequence(b, b + count), b, b + count); + part(onNewPart, newText.subSequence(a, a + count), a, a + count); + part(onOldPart, oldText.subSequence(b, b + count), b, b + count); } a += count; b += count; @@ -822,10 +852,10 @@ private void diff(final CharSequence oldText, final CharSequence newText, Region if (equal != thisEqual || i == minLength) { if (i - start > 0) { if (equal) { - onEqualPart.run(newText.subSequence(start, i), start, i); + part(onEqualPart, newText.subSequence(start, i), start, i); } else { - onNewPart.run(newText.subSequence(start, i), start, i); - onOldPart.run(oldText.subSequence(start, i), start, i); + part(onNewPart, newText.subSequence(start, i), start, i); + part(onOldPart, oldText.subSequence(start, i), start, i); } } equal = thisEqual; @@ -833,10 +863,10 @@ private void diff(final CharSequence oldText, final CharSequence newText, Region } } if (newText.length() - minLength > 0) { - onNewPart.run(newText.subSequence(minLength, newText.length()), minLength, newText.length()); + part(onNewPart, newText.subSequence(minLength, newText.length()), minLength, newText.length()); } if (oldText.length() - minLength > 0) { - onOldPart.run(oldText.subSequence(minLength, oldText.length()), minLength, oldText.length()); + part(onOldPart, oldText.subSequence(minLength, oldText.length()), minLength, oldText.length()); } } } else { @@ -859,11 +889,11 @@ private void diff(final CharSequence oldText, final CharSequence newText, Region } else { if (alen > 0) { // new part on [astart, a) - onNewPart.run(newText.subSequence(astart, a), astart, a); + part(onNewPart, newText.subSequence(astart, a), astart, a); } if (blen > 0) { // old part on [bstart, b) - onOldPart.run(oldText.subSequence(bstart, b), bstart, b); + part(onOldPart, oldText.subSequence(bstart, b), bstart, b); } } } @@ -1011,9 +1041,14 @@ public int getGravity() { } public void setAnimationProperties(float moveAmplitude, long startDelay, long duration, TimeInterpolator interpolator) { + setAnimationProperties(moveAmplitude, startDelay, duration, 1.0f, interpolator); + } + + public void setAnimationProperties(float moveAmplitude, long startDelay, long duration, float wave, TimeInterpolator interpolator) { this.moveAmplitude = moveAmplitude; animateDelay = startDelay; animateDuration = duration; + animateWave = wave; animateInterpolator = interpolator; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AttachableDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AttachableDrawable.java index c98b50cf017..b03c1b9ce3e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AttachableDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AttachableDrawable.java @@ -1,8 +1,12 @@ package org.telegram.ui.Components; +import android.view.View; + import org.telegram.messenger.ImageReceiver; public interface AttachableDrawable { void onAttachedToWindow(ImageReceiver parent); void onDetachedFromWindow(ImageReceiver parent); + + default void setParent(View view) {} } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarsDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarsDrawable.java index 05d09a59917..48d95dca6a8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarsDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarsDrawable.java @@ -17,6 +17,8 @@ import org.telegram.messenger.AccountInstance; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.DialogObject; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.ImageLocation; import org.telegram.messenger.ImageReceiver; import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; @@ -25,6 +27,7 @@ import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; +import org.telegram.tgnet.tl.TL_stories; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Cells.GroupCallUserCell; import org.telegram.ui.Stories.StoriesGradientTools; @@ -341,14 +344,35 @@ public void setObject(int index, int account, TLObject object) { animatingStates[index].avatarDrawable.setInfo(account, currentUser); } animatingStates[index].id = currentUser.id; - } else { + } else if (object instanceof TLRPC.Chat) { currentChat = (TLRPC.Chat) object; animatingStates[index].avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_NORMAL); animatingStates[index].avatarDrawable.setScaleSize(1f); animatingStates[index].avatarDrawable.setInfo(account, currentChat); animatingStates[index].id = -currentChat.id; } - if (currentUser != null) { + int size = getSize(); + if (object instanceof TL_stories.StoryItem) { + TL_stories.StoryItem story = (TL_stories.StoryItem) object; + animatingStates[index].id = story.id; + if (story.media.document != null) { + TLRPC.PhotoSize photoSize1 = FileLoader.getClosestPhotoSizeWithSize(story.media.document.thumbs, 50, true, null, false); + TLRPC.PhotoSize photoSize2 = FileLoader.getClosestPhotoSizeWithSize(story.media.document.thumbs, 50, true, photoSize1, true); + animatingStates[index].imageReceiver.setImage( + ImageLocation.getForDocument(photoSize2, story.media.document), size + "_" + size, + ImageLocation.getForDocument(photoSize1, story.media.document), size + "_" + size, + 0, null, story, 0 + ); + } else if (story.media.photo != null) { + TLRPC.PhotoSize photoSize1 = FileLoader.getClosestPhotoSizeWithSize(story.media.photo.sizes, 50, true, null, false); + TLRPC.PhotoSize photoSize2 = FileLoader.getClosestPhotoSizeWithSize(story.media.photo.sizes, 50, true, photoSize1, true); + animatingStates[index].imageReceiver.setImage( + ImageLocation.getForPhoto(photoSize2, story.media.photo), size + "_" + size, + ImageLocation.getForPhoto(photoSize1, story.media.photo), size + "_" + size, + 0, null, story, 0 + ); + } + } else if (currentUser != null) { if (currentUser.self && showSavedMessages) { animatingStates[index].imageReceiver.setImageBitmap(animatingStates[index].avatarDrawable); } else { @@ -357,7 +381,6 @@ public void setObject(int index, int account, TLObject object) { } else { animatingStates[index].imageReceiver.setForUserOrChat(currentChat, animatingStates[index].avatarDrawable); } - int size = getSize(); animatingStates[index].imageReceiver.setRoundRadius(size / 2); animatingStates[index].imageReceiver.setImageCoords(0, 0, size, size); invalidate(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BackButtonMenu.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BackButtonMenu.java index 3bb97c37c86..4a4b58a9595 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BackButtonMenu.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BackButtonMenu.java @@ -79,6 +79,7 @@ public static ActionBarPopupWindow show(BaseFragment thisFragment, View backButt AtomicReference scrimPopupWindowRef = new AtomicReference<>(); + boolean hadDialogs = false; for (int i = 0; i < dialogs.size(); ++i) { final PulledDialog pDialog = dialogs.get(i); final TLRPC.Chat chat = pDialog.chat; @@ -107,6 +108,7 @@ public static ActionBarPopupWindow show(BaseFragment thisFragment, View backButt Drawable thumb = avatarDrawable; boolean addDivider = false; if (topic != null) { + hadDialogs = true; if (topic.id == 1) { thumb = ForumUtilities.createGeneralTopicDrawable(fragmentView.getContext(), 1f, Theme.getColor(Theme.key_chat_inMenu, resourcesProvider), false); imageView.setImageDrawable(thumb); @@ -119,6 +121,7 @@ public static ActionBarPopupWindow show(BaseFragment thisFragment, View backButt } titleView.setText(topic.title); } else if (chat != null) { + hadDialogs = true; avatarDrawable.setInfo(thisFragment.getCurrentAccount(), chat); if (chat.photo != null && chat.photo.strippedBitmap != null) { thumb = chat.photo.strippedBitmap; @@ -126,6 +129,7 @@ public static ActionBarPopupWindow show(BaseFragment thisFragment, View backButt imageView.setImage(ImageLocation.getForChat(chat, ImageLocation.TYPE_SMALL), "50_50", thumb, chat); titleView.setText(chat.title); } else if (user != null) { + hadDialogs = true; String name; if (user.photo != null && user.photo.strippedBitmap != null) { thumb = user.photo.strippedBitmap; @@ -207,6 +211,8 @@ public static ActionBarPopupWindow show(BaseFragment thisFragment, View backButt } } + if (!hadDialogs) return null; + ActionBarPopupWindow scrimPopupWindow = new ActionBarPopupWindow(layout, LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT); scrimPopupWindowRef.set(scrimPopupWindow); scrimPopupWindow.setPauseNotifications(true); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java index cc74baa2e27..39c90492596 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java @@ -286,7 +286,7 @@ public AvatarDrawable getAvatarDrawable() { protected void onDetachedFromWindow() { super.onDetachedFromWindow(); attached = false; - imageReceiver.onDetachedFromWindow(); + if (applyAttach) imageReceiver.onDetachedFromWindow(); if (blurAllowed) { blurImageReceiver.onDetachedFromWindow(); } @@ -295,11 +295,13 @@ protected void onDetachedFromWindow() { } } + public boolean applyAttach = true; + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); attached = true; - imageReceiver.onAttachedToWindow(); + if (applyAttach) imageReceiver.onAttachedToWindow(); if (blurAllowed) { blurImageReceiver.onAttachedToWindow(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurringShader.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurringShader.java index 4fda8b778f9..83cc66a0e91 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurringShader.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurringShader.java @@ -647,7 +647,9 @@ public Bitmap getBitmap(Bitmap bitmap, String key, int orientation, int invert, } canvas.rotate(orientation); canvas.translate(-padding - width / 2f, -padding - height / 2f); - canvas.drawBitmap(bitmap, src, dst, null); + try { + canvas.drawBitmap(bitmap, src, dst, null); + } catch (Exception e) {} Utilities.stackBlurBitmap(resultBitmap, 6); if (padding > 0) { // clear borders diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Bulletin.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Bulletin.java index c5580b76f92..1176139e2e0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Bulletin.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Bulletin.java @@ -99,6 +99,7 @@ public class Bulletin { private SpringAnimation bottomOffsetSpring; public static Bulletin make(@NonNull FrameLayout containerLayout, @NonNull Layout contentLayout, int duration) { + if (containerLayout == null) return new EmptyBulletin(); return new Bulletin(null, containerLayout, contentLayout, duration); } @@ -110,7 +111,10 @@ public Bulletin setOnClickListener(View.OnClickListener onClickListener) { } @SuppressLint("RtlHardcoded") - public static Bulletin make(@NonNull BaseFragment fragment, @NonNull Layout contentLayout, int duration) { + public static Bulletin make(@Nullable BaseFragment fragment, @NonNull Layout contentLayout, int duration) { + if (fragment == null) { + return new EmptyBulletin(); + } if (fragment instanceof ChatActivity) { contentLayout.setWideScreenParams(ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL); } else if (fragment instanceof DialogsActivity) { 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 d72644a511d..7b74cf56165 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BulletinFactory.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BulletinFactory.java @@ -1,5 +1,7 @@ package org.telegram.ui.Components; +import static org.telegram.messenger.AndroidUtilities.dp; + import android.app.DownloadManager; import android.content.Context; import android.content.Intent; @@ -36,6 +38,8 @@ import org.telegram.messenger.BuildVars; import org.telegram.messenger.ChatObject; import org.telegram.messenger.DialogObject; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.ImageLocation; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MessageObject; @@ -88,9 +92,22 @@ public static BulletinFactory global() { return BulletinFactory.of(baseFragment); } + public Bulletin makeForError(TLRPC.TL_error error) { + if (!LaunchActivity.isActive) return new Bulletin.EmptyBulletin(); + if (error == null) { + return createErrorBulletin(LocaleController.formatString(R.string.UnknownError)); + } else { + return createErrorBulletin(LocaleController.formatString(R.string.UnknownErrorCode, error.text)); + } + } + public void showForError(TLRPC.TL_error error) { if (!LaunchActivity.isActive) return; - createErrorBulletin(LocaleController.formatString(R.string.UnknownErrorCode, error.text)).show(); + if (error == null) { + createErrorBulletin(LocaleController.formatString(R.string.UnknownError)).show(); + } else { + createErrorBulletin(LocaleController.formatString(R.string.UnknownErrorCode, error.text)).show(); + } } public static void showError(TLRPC.TL_error error) { @@ -196,6 +213,54 @@ public Bulletin createSimpleBulletin(int iconRawId, CharSequence text) { return createSimpleBulletinWithIconSize(iconRawId, text, 36); } + public Bulletin createSimpleBulletin(TLRPC.MessageMedia media, CharSequence text) { + if (media == null) return new Bulletin.EmptyBulletin(); + if (media.document != null) + return createSimpleBulletin(media.document, text); + if (media.photo != null) + return createSimpleBulletin(media.photo, text); + return new Bulletin.EmptyBulletin(); + } + + public Bulletin createSimpleBulletin(TLRPC.Document document, CharSequence text) { + if (document == null) return new Bulletin.EmptyBulletin(); + final Bulletin.TwoLineLayout layout = new Bulletin.TwoLineLayout(getContext(), resourcesProvider); + TLRPC.PhotoSize thumbSize = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, dp(28), true, null, false); + TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, dp(28), true, thumbSize, true); + layout.imageView.setImage( + ImageLocation.getForDocument(photoSize, document), "28_28", + ImageLocation.getForDocument(thumbSize, document), "28_28", + null, 0, 0, null + ); + layout.imageView.getImageReceiver().setRoundRadius(dp(5)); + layout.titleTextView.setText(text); + layout.titleTextView.setSingleLine(true); + layout.titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); + layout.titleTextView.setMaxLines(1); + layout.titleTextView.setTypeface(null); + layout.subtitleTextView.setVisibility(View.GONE); + return create(layout, text.length() < 20 ? Bulletin.DURATION_SHORT : Bulletin.DURATION_LONG); + } + + public Bulletin createSimpleBulletin(TLRPC.Photo photo, CharSequence text) { + if (photo == null) return new Bulletin.EmptyBulletin(); + final Bulletin.TwoLineLayout layout = new Bulletin.TwoLineLayout(getContext(), resourcesProvider); + TLRPC.PhotoSize thumbSize = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, dp(28), true, null, false); + TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, dp(28), true, thumbSize, true); + layout.imageView.setImage( + ImageLocation.getForPhoto(photoSize, photo), "28_28", + ImageLocation.getForPhoto(thumbSize, photo), "28_28", + null, 0, 0, null + ); + layout.imageView.getImageReceiver().setRoundRadius(dp(5)); + layout.titleTextView.setText(text); + layout.titleTextView.setSingleLine(true); + layout.titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); + layout.titleTextView.setMaxLines(1); + layout.subtitleTextView.setVisibility(View.GONE); + return create(layout, text.length() < 20 ? Bulletin.DURATION_SHORT : Bulletin.DURATION_LONG); + } + public Bulletin createSimpleBulletinWithIconSize(int iconRawId, CharSequence text, int iconSize) { final Bulletin.LottieLayout layout = new Bulletin.LottieLayout(getContext(), resourcesProvider); layout.setAnimation(iconRawId, iconSize, iconSize); @@ -224,8 +289,8 @@ public Bulletin createImageBulletin(int iconRawId, CharSequence title) { layout.textView.setLines(2); layout.textView.setMaxLines(4); layout.textView.setMaxWidth(HintView2.cutInFancyHalf(layout.textView.getText(), layout.textView.getPaint())); - layout.textView.setLineSpacing(AndroidUtilities.dp(1.33f), 1f); - ((ViewGroup.MarginLayoutParams) layout.textView.getLayoutParams()).rightMargin = AndroidUtilities.dp(12); + layout.textView.setLineSpacing(dp(1.33f), 1f); + ((ViewGroup.MarginLayoutParams) layout.textView.getLayoutParams()).rightMargin = dp(12); layout.setWrapWidth(); return create(layout, Bulletin.DURATION_PROLONG); } @@ -307,7 +372,7 @@ public Bulletin createSimpleBulletin(int iconRawId, CharSequence text, CharSeque layout.setAnimation(iconRawId, 36, 36); } else { layout.imageView.setVisibility(View.INVISIBLE); - ((ViewGroup.MarginLayoutParams) layout.textView.getLayoutParams()).leftMargin = AndroidUtilities.dp(16); + ((ViewGroup.MarginLayoutParams) layout.textView.getLayoutParams()).leftMargin = dp(16); } layout.textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); layout.textView.setTextDirection(View.TEXT_DIRECTION_LOCALE); @@ -424,7 +489,7 @@ public Bulletin createUsersBulletin(List users, CharSequence } } if (users.size() == 1) { - layout.avatarsImageView.setTranslationX(AndroidUtilities.dp(4)); + layout.avatarsImageView.setTranslationX(dp(4)); layout.avatarsImageView.setScaleX(1.2f); layout.avatarsImageView.setScaleY(1.2f); } else { @@ -443,9 +508,9 @@ public Bulletin createUsersBulletin(List users, CharSequence layout.subtitleView.setSingleLine(false); layout.subtitleView.setMaxLines(3); if (layout.linearLayout.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { - int margin = AndroidUtilities.dp(12 + 56 + 2 - (3 - count) * 12); + int margin = dp(12 + 56 + 2 - (3 - count) * 12); if (count == 1) { - margin += AndroidUtilities.dp(4); + margin += dp(4); } if (LocaleController.isRTL) { ((ViewGroup.MarginLayoutParams) layout.linearLayout.getLayoutParams()).rightMargin = margin; @@ -458,10 +523,10 @@ public Bulletin createUsersBulletin(List users, CharSequence layout.textView.setMaxLines(2); layout.textView.setText(text); if (layout.textView.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { - int margin = AndroidUtilities.dp(12 + 56 + 2 - (3 - count) * 12); + int margin = dp(12 + 56 + 2 - (3 - count) * 12); if (count == 1) { - layout.textView.setTranslationY(-AndroidUtilities.dp(1)); - margin += AndroidUtilities.dp(4); + layout.textView.setTranslationY(-dp(1)); + margin += dp(4); } if (LocaleController.isRTL) { ((ViewGroup.MarginLayoutParams) layout.textView.getLayoutParams()).rightMargin = margin; @@ -492,7 +557,7 @@ public Bulletin createChatsBulletin(List objects, CharSequence text, C } } if (objects.size() == 1) { - layout.avatarsImageView.setTranslationX(AndroidUtilities.dp(4)); + layout.avatarsImageView.setTranslationX(dp(4)); layout.avatarsImageView.setScaleX(1.2f); layout.avatarsImageView.setScaleY(1.2f); } else { @@ -510,7 +575,7 @@ public Bulletin createChatsBulletin(List objects, CharSequence text, C layout.subtitleView.setSingleLine(true); layout.subtitleView.setMaxLines(1); if (layout.linearLayout.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { - int margin = AndroidUtilities.dp(12 + 56 + 6 - (3 - count) * 12); + int margin = dp(12 + 56 + 6 - (3 - count) * 12); if (LocaleController.isRTL) { ((ViewGroup.MarginLayoutParams) layout.linearLayout.getLayoutParams()).rightMargin = margin; } else { @@ -522,7 +587,7 @@ public Bulletin createChatsBulletin(List objects, CharSequence text, C layout.textView.setMaxLines(2); layout.textView.setText(text); if (layout.textView.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { - int margin = AndroidUtilities.dp(12 + 56 + 6 - (3 - count) * 12); + int margin = dp(12 + 56 + 6 - (3 - count) * 12); if (LocaleController.isRTL) { ((ViewGroup.MarginLayoutParams) layout.textView.getLayoutParams()).rightMargin = margin; } else { @@ -531,7 +596,7 @@ public Bulletin createChatsBulletin(List objects, CharSequence text, C } } if (LocaleController.isRTL) { - layout.avatarsImageView.setTranslationX(AndroidUtilities.dp(32 - (count - 1) * 12)); + layout.avatarsImageView.setTranslationX(dp(32 - (count - 1) * 12)); } return create(layout, Bulletin.DURATION_PROLONG); @@ -627,7 +692,7 @@ public Bulletin createEmojiBulletin(TLRPC.Document document, CharSequence text, } layout.setAnimation(document, 36, 36); if (layout.imageView.getImageReceiver() != null) { - layout.imageView.getImageReceiver().setRoundRadius(AndroidUtilities.dp(4)); + layout.imageView.getImageReceiver().setRoundRadius(dp(4)); } layout.textView.setText(text); layout.textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); @@ -677,7 +742,7 @@ public Bulletin createContainsEmojiBulletin(TLRPC.Document document, int type, U LoadingSpan loadingSpan = null; int index; if ((index = stringBuilder.toString().indexOf(loadingPlaceholder)) >= 0) { - stringBuilder.setSpan(loadingSpan = new LoadingSpan(null, AndroidUtilities.dp(100), AndroidUtilities.dp(2), resourcesProvider), index, index + loadingPlaceholder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + stringBuilder.setSpan(loadingSpan = new LoadingSpan(null, dp(100), dp(2), resourcesProvider), index, index + loadingPlaceholder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); loadingSpan.setColors( ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_undo_infoColor, resourcesProvider), 0x20), ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_undo_infoColor, resourcesProvider), 0x48) @@ -1310,10 +1375,10 @@ public Bulletin createMessagesTaggedBulletin(int messagesCount, TLRPC.Document d layout.textView.setTypeface(Typeface.SANS_SERIF); layout.textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); layout.textView.setEllipsize(TextUtils.TruncateAt.END); - layout.textView.setPadding(0, 0, 0, AndroidUtilities.dp(8)); + layout.textView.setPadding(0, 0, 0, dp(8)); TextPaint textPaint = new TextPaint(); - textPaint.setTextSize(AndroidUtilities.dp(20)); + textPaint.setTextSize(dp(20)); SpannableString spannable = new SpannableString("d"); spannable.setSpan(new AnimatedEmojiSpan(document, textPaint.getFontMetricsInt()), 0, spannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); layout.textView.setText( 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 3e9a3661957..c17722002ed 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java @@ -3249,21 +3249,22 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto TLRPC.User user = getParentFragment().getCurrentUser(); if (user == null) return; + final boolean birthday = getParentFragment().getCurrentUserInfo() != null && BirthdayController.isToday(getParentFragment().getCurrentUserInfo().birthday); ArrayList options = new ArrayList<>(getParentFragment().getCurrentUserInfo().premium_gifts); if (options.isEmpty()) { final AlertDialog progressDialog = new AlertDialog(getContext(), AlertDialog.ALERT_TYPE_SPINNER); + progressDialog.showDelayed(200); final int reqId = BoostRepository.loadGiftOptions(currentAccount, null, loadedOptions -> { progressDialog.dismiss(); loadedOptions = BoostRepository.filterGiftOptions(loadedOptions, 1); loadedOptions = BoostRepository.filterGiftOptionsByBilling(loadedOptions); - new GiftSheet(getContext(), currentAccount, user.id, loadedOptions, null).show(); + new GiftSheet(getContext(), currentAccount, user.id, loadedOptions, null).setBirthday(birthday).show(); }); progressDialog.setOnCancelListener(di -> { parentFragment.getConnectionsManager().cancelRequest(reqId, true); }); - progressDialog.showDelayed(200); } else { - new GiftSheet(getContext(), currentAccount, user.id, null, null).show(); + new GiftSheet(getContext(), currentAccount, user.id, null, null).setBirthday(birthday).show(); } }); } @@ -3970,7 +3971,7 @@ private void openWebViewMenu() { createBotWebViewMenuContainer(); Runnable onRequestWebView = () -> { AndroidUtilities.hideKeyboard(this); - WebViewRequestProps props = WebViewRequestProps.of(currentAccount, dialog_id, dialog_id, botMenuWebViewTitle, botMenuWebViewUrl, BotWebViewAttachedSheet.TYPE_BOT_MENU_BUTTON, 0, false, null, false, null, null, 0, false); + WebViewRequestProps props = WebViewRequestProps.of(currentAccount, dialog_id, dialog_id, botMenuWebViewTitle, botMenuWebViewUrl, BotWebViewAttachedSheet.TYPE_BOT_MENU_BUTTON, 0, false, null, false, null, null, 0, false, false); if (LaunchActivity.instance != null && LaunchActivity.instance.getBottomSheetTabs() != null && LaunchActivity.instance.getBottomSheetTabs().tryReopenTab(props) != null) { if (botCommandsMenuButton != null) { botCommandsMenuButton.setOpened(false); @@ -3986,10 +3987,10 @@ private void openWebViewMenu() { } }); }); - Browser.openAsInternalIntent(getContext(), botMenuWebViewUrl, false, progress); + Browser.openAsInternalIntent(getContext(), botMenuWebViewUrl, false, false, progress); return; } - if (AndroidUtilities.isTablet()) { +// if (AndroidUtilities.isTablet() || true) { BotWebViewSheet webViewSheet = new BotWebViewSheet(getContext(), resourcesProvider); webViewSheet.setDefaultFullsize(false); webViewSheet.setNeedsContext(true); @@ -4000,20 +4001,20 @@ private void openWebViewMenu() { if (botCommandsMenuButton != null) { botCommandsMenuButton.setOpened(false); } - } else { - if (parentFragment != null && parentFragment.getParentActivity() != null) { - BotWebViewAttachedSheet sheet = parentFragment.createBotViewer(); - sheet.setDefaultFullsize(false); - sheet.setNeedsContext(false); - sheet.setParentActivity(parentFragment.getParentActivity()); - sheet.requestWebView(parentFragment, props); - sheet.show(); - - if (botCommandsMenuButton != null) { - botCommandsMenuButton.setOpened(false); - } - } - } +// } else { +// if (parentFragment != null && parentFragment.getParentActivity() != null) { +// BotWebViewAttachedSheet sheet = parentFragment.createBotViewer(); +// sheet.setDefaultFullsize(false); +// sheet.setNeedsContext(false); +// sheet.setParentActivity(parentFragment.getParentActivity()); +// sheet.requestWebView(parentFragment, props); +// sheet.show(); +// +// if (botCommandsMenuButton != null) { +// botCommandsMenuButton.setOpened(false); +// } +// } +// } }; if (SharedPrefsHelper.isWebViewConfirmShown(currentAccount, dialog_id)) { @@ -9913,26 +9914,26 @@ public void run() { return; } - final WebViewRequestProps props = WebViewRequestProps.of(currentAccount, messageObject.messageOwner.dialog_id, botId, button.text, button.url, button instanceof TLRPC.TL_keyboardButtonSimpleWebView ? BotWebViewAttachedSheet.TYPE_SIMPLE_WEB_VIEW_BUTTON : BotWebViewAttachedSheet.TYPE_WEB_VIEW_BUTTON, replyMessageObject != null ? replyMessageObject.messageOwner.id : 0, false, null, false, null, null, 0, false); + final WebViewRequestProps props = WebViewRequestProps.of(currentAccount, messageObject.messageOwner.dialog_id, botId, button.text, button.url, button instanceof TLRPC.TL_keyboardButtonSimpleWebView ? BotWebViewAttachedSheet.TYPE_SIMPLE_WEB_VIEW_BUTTON : BotWebViewAttachedSheet.TYPE_WEB_VIEW_BUTTON, replyMessageObject != null ? replyMessageObject.messageOwner.id : 0, false, null, false, null, null, 0, false, false); if (LaunchActivity.instance != null && LaunchActivity.instance.getBottomSheetTabs() != null && LaunchActivity.instance.getBottomSheetTabs().tryReopenTab(props) != null) { if (botCommandsMenuButton != null) { botCommandsMenuButton.setOpened(false); } return; } - if (AndroidUtilities.isTablet()) { +// if (AndroidUtilities.isTablet() || true) { BotWebViewSheet webViewSheet = new BotWebViewSheet(getContext(), resourcesProvider); webViewSheet.setParentActivity(parentActivity); webViewSheet.requestWebView(parentFragment, props); webViewSheet.show(); - } else { - BotWebViewAttachedSheet webViewSheet = parentFragment.createBotViewer(); - webViewSheet.setDefaultFullsize(false); - webViewSheet.setNeedsContext(true); - webViewSheet.setParentActivity(parentActivity); - webViewSheet.requestWebView(parentFragment, props); - webViewSheet.show(); - } +// } else { +// BotWebViewAttachedSheet webViewSheet = parentFragment.createBotViewer(); +// webViewSheet.setDefaultFullsize(false); +// webViewSheet.setNeedsContext(true); +// webViewSheet.setParentActivity(parentActivity); +// webViewSheet.requestWebView(parentFragment, props); +// webViewSheet.show(); +// } } }; if (SharedPrefsHelper.isWebViewConfirmShown(currentAccount, botId)) { @@ -10377,7 +10378,7 @@ public void onShowStickerSet(TLRPC.StickerSet stickerSet, TLRPC.InputStickerSet inputStickerSet.access_hash = stickerSet.access_hash; inputStickerSet.id = stickerSet.id; } - StickersAlert alert = new StickersAlert(parentActivity, fragment, inputStickerSet, null, ChatActivityEnterView.this, resourcesProvider); + StickersAlert alert = new StickersAlert(parentActivity, fragment, inputStickerSet, null, ChatActivityEnterView.this, resourcesProvider, false); fragment.showDialog(alert); if (edit) { alert.enableEditMode(); 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 293e007608f..453b34dd28c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java @@ -873,6 +873,7 @@ public void onPanTransitionEnd() { private TextView botMainButtonTextView; private float botMainButtonOffsetY; + private int editType; protected MessageObject editingMessageObject; private boolean buttonPressed; @@ -2029,6 +2030,8 @@ public void onItemClick(int id) { selectedMenuItem.setContentDescription(getString(R.string.AccDescrMoreOptions)); selectedMenuItem.setVisibility(View.INVISIBLE); selectedMenuItem.setAlpha(0.0f); + selectedMenuItem.setScaleX(0.6f); + selectedMenuItem.setScaleY(0.6f); selectedMenuItem.setSubMenuOpenSide(2); selectedMenuItem.setDelegate(id -> actionBar.getActionBarMenuOnItemClick().onItemClick(id)); selectedMenuItem.setAdditionalYOffset(AndroidUtilities.dp(72)); @@ -2809,7 +2812,7 @@ public int getFillColor() { } }); writeButton.setOnLongClickListener(view -> { - if ((dialogId == 0 && !(baseFragment instanceof ChatActivity)) || editingMessageObject != null || currentLimit - codepointCount < 0) { + if ((dialogId == 0 && !(baseFragment instanceof ChatActivity)) || currentLimit - codepointCount < 0) { return false; } ChatActivity chatActivity = null; @@ -3121,10 +3124,12 @@ R.raw.position_above, getString(R.string.CaptionBelow), messageSendPreview.scrollTo(!captionAbove); }); options.addView(button); - options.addGap(); + if (editingMessageObject == null) { + options.addGap(); + } } final boolean self = UserObject.isUserSelf(user); - if ((chatActivity != null && chatActivity.canScheduleMessage()) || currentAttachLayout.canScheduleMessages()) { + if (editingMessageObject == null && ((chatActivity != null && chatActivity.canScheduleMessage()) || currentAttachLayout.canScheduleMessages())) { final long finalDialogId = dialogId; options.add(R.drawable.msg_calendar2, getString(self ? R.string.SetReminder : R.string.ScheduleMessage), () -> { AlertsCreator.createScheduleDatePickerDialog(getContext(), finalDialogId, (notify, scheduleDate) -> { @@ -3142,7 +3147,7 @@ R.raw.position_above, getString(R.string.CaptionBelow), }, resourcesProvider); }); } - if (!self) { + if (editingMessageObject == null && !self) { options.add(R.drawable.input_notify_off, getString(R.string.SendWithoutSound), () -> { final long effectId = messageSendPreview != null ? messageSendPreview.getSelectedEffect() : 0; if (messageSendPreview != null) { @@ -3157,7 +3162,7 @@ R.raw.position_above, getString(R.string.CaptionBelow), } }); } - if (canHaveStars && chatActivity != null && ChatObject.isChannelAndNotMegaGroup(chatActivity.getCurrentChat()) && chatActivity.getCurrentChatInfo() != null && chatActivity.getCurrentChatInfo().paid_media_allowed) { + if (editingMessageObject == null && canHaveStars && chatActivity != null && ChatObject.isChannelAndNotMegaGroup(chatActivity.getCurrentChat()) && chatActivity.getCurrentChatInfo() != null && chatActivity.getCurrentChatInfo().paid_media_allowed) { ActionBarMenuSubItem item = options.add(R.drawable.menu_feature_paid, getString(R.string.PaidMediaButton), null).getLast(); item.setOnClickListener(v -> { if (photoLayout == null) return; @@ -3189,7 +3194,7 @@ R.raw.position_above, getString(R.string.CaptionBelow), messageSendPreview.setItemOptions(options); messageSendPreview.setMessageObjects(messageObjects); - if (dialogId >= 0 && hasMessageToEffect) { + if (editingMessageObject == null && dialogId >= 0 && hasMessageToEffect) { messageSendPreview.allowEffectSelector(parentFragment); } @@ -3394,11 +3399,30 @@ public void show() { } } - public void setEditingMessageObject(MessageObject messageObject) { - if (editingMessageObject == messageObject) { + public static final int EDITMEDIA_TYPE_ANY = -1; + public static final int EDITMEDIA_TYPE_PHOTOVIDEO = 0; + public static final int EDITMEDIA_TYPE_FILE = 1; + public static final int EDITMEDIA_TYPE_MUSIC = 2; + + public void setEditingMessageObject(int type, MessageObject messageObject) { + if (messageObject != null) { + if (photoLayout != null) { + photoLayout.clearSelectedPhotos(); + } + } + if (editingMessageObject == messageObject && editType == type) { return; } editingMessageObject = messageObject; + if (editingMessageObject != null && editingMessageObject.hasValidGroupId()) { + if (editingMessageObject.isMusic()) + type = EDITMEDIA_TYPE_MUSIC; + else if (editingMessageObject.isDocument()) + type = EDITMEDIA_TYPE_FILE; + else + type = EDITMEDIA_TYPE_PHOTOVIDEO; + } + editType = type; if (editingMessageObject != null) { maxSelectedPhotos = 1; allowOrder = false; @@ -3407,6 +3431,7 @@ public void setEditingMessageObject(MessageObject messageObject) { allowOrder = true; } buttonsAdapter.notifyDataSetChanged(); + updateCountButton(0); } public MessageObject getEditingMessageObject() { @@ -4049,8 +4074,13 @@ protected boolean onCustomOpenAnimation() { appearSpringAnimation.cancel(); } appearSpringAnimation = new SpringAnimation(super.containerView, DynamicAnimation.TRANSLATION_Y, 0); - appearSpringAnimation.getSpring().setDampingRatio(0.75f); - appearSpringAnimation.getSpring().setStiffness(350.0f); + if (editingMessageObject != null) { + appearSpringAnimation.getSpring().setDampingRatio(0.75f); + appearSpringAnimation.getSpring().setStiffness(350.0f); + } else { + appearSpringAnimation.getSpring().setDampingRatio(0.75f); + appearSpringAnimation.getSpring().setStiffness(350.0f); + } appearSpringAnimation.start(); if (Build.VERSION.SDK_INT >= 20 && useHardwareLayer) { @@ -4374,7 +4404,7 @@ private void updateActionBarVisibility(boolean show, boolean animated) { actionBarAnimation = null; } - boolean needsSearchItem = searchItem != null && (avatarSearch || false && currentAttachLayout == photoLayout && !menuShowed && baseFragment instanceof ChatActivity && ((ChatActivity) baseFragment).allowSendGifs() && ((ChatActivity) baseFragment).allowSendPhotos()); + boolean needsSearchItem = searchItem != null && false && (avatarSearch || currentAttachLayout == photoLayout && !menuShowed && baseFragment instanceof ChatActivity && ((ChatActivity) baseFragment).allowSendGifs() && ((ChatActivity) baseFragment).allowSendPhotos()); boolean needMoreItem = !isPhotoPicker && !storyMediaPicker && (avatarPicker != 0 || !menuShowed) && currentAttachLayout == photoLayout && (photosEnabled || videosEnabled); if (currentAttachLayout == restrictedLayout) { needsSearchItem = false; @@ -4386,6 +4416,7 @@ private void updateActionBarVisibility(boolean show, boolean animated) { } if (needMoreItem) { selectedMenuItem.setVisibility(View.VISIBLE); + selectedMenuItem.setClickable(true); } } else if (typeButtonsAvailable && frameLayout2.getTag() == null) { buttonsRecyclerView.setVisibility(View.VISIBLE); @@ -4451,6 +4482,8 @@ public void onAnimationCancel(Animator animation) { } if (needMoreItem) { selectedMenuItem.setAlpha(show ? 1.0f : 0.0f); + selectedMenuItem.setScaleX(show ? 1.0f : 0.6f); + selectedMenuItem.setScaleY(show ? 1.0f : 0.6f); } if (!show) { if (searchItem != null) { @@ -4520,6 +4553,15 @@ public void updateCountButton(int animated) { } currentAttachLayout.onSelectedItemsCountChanged(count); +// if (editingMessageObject != null) { +// menuShowed = count > 0 && currentAttachLayout == photoLayout; +// selectedTextView.setText(LocaleController.getString(R.string.ChoosePhotoOrVideo)); +// headerView.setAlpha(currentAttachLayout == photoLayout ? 1f : 0f); +// headerView.setVisibility(currentAttachLayout == photoLayout ? View.VISIBLE : View.INVISIBLE); +// selectedMenuItem.setVisibility(View.VISIBLE); +// selectedMenuItem.setClickable(count > 0); +// selectedMenuItem.animate().alpha(count > 0 ? 1f : 0f).scaleX(count > 0 ? 1f : .6f).scaleY(count > 0 ? 1f : .6f).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).setDuration(320).start(); +// } else if (currentAttachLayout == photoLayout && ((baseFragment instanceof ChatActivity) || avatarPicker != 0 || storyMediaPicker) && (count == 0 && menuShowed || (count != 0 || avatarPicker != 0 || storyMediaPicker) && !menuShowed)) { menuShowed = count != 0 || avatarPicker != 0 || storyMediaPicker; if (menuAnimator != null) { @@ -4530,6 +4572,7 @@ public void updateCountButton(int animated) { if (menuShowed) { if (avatarPicker == 0 && !storyMediaPicker) { selectedMenuItem.setVisibility(View.VISIBLE); + selectedMenuItem.setClickable(true); } headerView.setVisibility(View.VISIBLE); } else { @@ -4540,6 +4583,8 @@ public void updateCountButton(int animated) { if (animated == 0) { if (actionBar.getTag() == null && avatarPicker == 0 && !storyMediaPicker) { selectedMenuItem.setAlpha(menuShowed ? 1.0f : 0.0f); + selectedMenuItem.setScaleX(menuShowed ? 1.0f : 0.6f); + selectedMenuItem.setScaleY(menuShowed ? 1.0f : 0.6f); } headerView.setAlpha(menuShowed ? 1.0f : 0.0f); if (needsSearchItem) { @@ -4553,6 +4598,8 @@ public void updateCountButton(int animated) { ArrayList animators = new ArrayList<>(); if (actionBar.getTag() == null && avatarPicker == 0 && !storyMediaPicker) { animators.add(ObjectAnimator.ofFloat(selectedMenuItem, View.ALPHA, menuShowed ? 1.0f : 0.0f)); + animators.add(ObjectAnimator.ofFloat(selectedMenuItem, View.SCALE_X, menuShowed ? 1.0f : 0.6f)); + animators.add(ObjectAnimator.ofFloat(selectedMenuItem, View.SCALE_Y, menuShowed ? 1.0f : 0.6f)); } animators.add(ObjectAnimator.ofFloat(headerView, View.ALPHA, menuShowed ? 1.0f : 0.0f)); if (needsSearchItem) { @@ -4649,17 +4696,36 @@ public void init() { openDocumentsLayout(false); layoutToSet = documentLayout; selectedId = 4; - } else if (editingMessageObject != null && (editingMessageObject.isMusic() || (editingMessageObject.isDocument() && !editingMessageObject.isGif()))) { - if (editingMessageObject.isMusic()) { - openAudioLayout(false); - layoutToSet = audioLayout; - selectedId = 3; + } else if (editingMessageObject != null) { + if (editType == EDITMEDIA_TYPE_ANY) { + typeButtonsAvailable = true; + if (editingMessageObject.isMusic()) { + openAudioLayout(false); + layoutToSet = audioLayout; + selectedId = 3; + } else if (editingMessageObject.isDocument()) { + openDocumentsLayout(false); + layoutToSet = documentLayout; + selectedId = 4; + } else { + layoutToSet = photoLayout; + selectedId = 1; + } } else { - openDocumentsLayout(false); - layoutToSet = documentLayout; - selectedId = 4; + if (editType == EDITMEDIA_TYPE_MUSIC) { + openAudioLayout(false); + layoutToSet = audioLayout; + selectedId = 3; + } else if (editType == EDITMEDIA_TYPE_FILE) { + openDocumentsLayout(false); + layoutToSet = documentLayout; + selectedId = 4; + } else { + layoutToSet = photoLayout; + selectedId = 1; + } + typeButtonsAvailable = false; } - typeButtonsAvailable = !editingMessageObject.hasValidGroupId(); } else { layoutToSet = photoLayout; typeButtonsAvailable = avatarPicker == 0 && !storyMediaPicker; @@ -4800,6 +4866,10 @@ public void enableStickerMode(Utilities.Callback2 c if (optionsItem != null) { selectedTextView.setTranslationY(-AndroidUtilities.dp(8)); optionsItem.setVisibility(View.VISIBLE); + optionsItem.setClickable(true); + optionsItem.setAlpha(1f); + optionsItem.setScaleX(1f); + optionsItem.setScaleY(1f); } } @@ -5006,16 +5076,20 @@ public void notifyDataSetChanged() { musicButton = buttonsCount++; } } else if (editingMessageObject != null) { - if ((editingMessageObject.isMusic() || editingMessageObject.isDocument()) && editingMessageObject.hasValidGroupId()) { - if (editingMessageObject.isMusic()) { - musicButton = buttonsCount++; - } else { - documentButton = buttonsCount++; - } - } else { + if (editType == EDITMEDIA_TYPE_ANY) { galleryButton = buttonsCount++; documentButton = buttonsCount++; musicButton = buttonsCount++; + } else { + if (editType == EDITMEDIA_TYPE_PHOTOVIDEO) { + galleryButton = buttonsCount++; + } + if (editType == EDITMEDIA_TYPE_FILE) { + documentButton = buttonsCount++; + } + if (editType == EDITMEDIA_TYPE_MUSIC) { + musicButton = buttonsCount++; + } } } else { galleryButton = buttonsCount++; @@ -5313,8 +5387,10 @@ protected void setupMentionContainer() { mentionContainer.getAdapter().setAllowChats(false); mentionContainer.getAdapter().setSearchInDailogs(true); if (baseFragment instanceof ChatActivity) { - mentionContainer.getAdapter().setChatInfo(((ChatActivity) baseFragment).getCurrentChatInfo()); - mentionContainer.getAdapter().setNeedUsernames(((ChatActivity) baseFragment).getCurrentChat() != null); + ChatActivity chatActivity = (ChatActivity) baseFragment; + mentionContainer.getAdapter().setUserOrChat(chatActivity.getCurrentUser(), chatActivity.getCurrentChat()); + mentionContainer.getAdapter().setChatInfo(chatActivity.getCurrentChatInfo()); + mentionContainer.getAdapter().setNeedUsernames(chatActivity.getCurrentChat() != null); } else { mentionContainer.getAdapter().setChatInfo(null); mentionContainer.getAdapter().setNeedUsernames(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 61ba5ce1274..20989c929da 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java @@ -538,6 +538,13 @@ public void sendButtonPressed(int index, VideoEditedInfo videoEditedInfo, boolea public boolean allowCaption() { return !parentAlert.isPhotoPicker; } + + @Override + public long getDialogId() { + if (parentAlert.baseFragment instanceof ChatActivity) + return ((ChatActivity) parentAlert.baseFragment).getDialogId(); + return super.getDialogId(); + } }; protected void updateCheckedPhotoIndices() { @@ -1692,7 +1699,7 @@ private void updatePhotoStarsPrice() { }); } - private void clearSelectedPhotos() { + public void clearSelectedPhotos() { spoilerItem.setText(LocaleController.getString(R.string.EnablePhotoSpoiler)); spoilerItem.setAnimatedIcon(R.raw.photo_spoiler); parentAlert.selectedMenuItem.showSubItem(compress); @@ -3348,7 +3355,7 @@ public void onSelectedItemsCountChanged(int count) { parentAlert.selectedMenuItem.showSubItem(open_in); hasCompress = false; parentAlert.selectedMenuItem.hideSubItem(compress); - } else if (documentsEnabled && getStarsPrice() <= 0) { + } else if (documentsEnabled && getStarsPrice() <= 0 && parentAlert.editingMessageObject == null) { hasCompress = true; parentAlert.selectedMenuItem.showSubItem(compress); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatSearchTabs.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatSearchTabs.java index fb1fe22f6e9..11a3515a4d1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatSearchTabs.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatSearchTabs.java @@ -52,9 +52,11 @@ protected void onShownUpdate(boolean finish) { } + private boolean shown; private float actionBarTagsT; private ValueAnimator actionBarTagsAnimator; public void show(boolean show) { + shown = show; if (actionBarTagsAnimator != null) { Animator a = actionBarTagsAnimator; actionBarTagsAnimator = null; @@ -86,13 +88,16 @@ public void onAnimationEnd(Animator animation) { actionBarTagsAnimator.start(); } + public boolean isShown() { + return shown; + } + public boolean shown() { return shownT > 0.5f; } public int getCurrentHeight() { return (int) (getMeasuredHeight() * shownT); } - private Paint backgroundPaint2; @Override public void setBackgroundColor(int color) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/CircularProgressDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/CircularProgressDrawable.java index e1b69c43fac..eecc2be5f5b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/CircularProgressDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/CircularProgressDrawable.java @@ -32,7 +32,7 @@ public CircularProgressDrawable(float size, float thickness, int color) { } private long start = -1; - private static final FastOutSlowInInterpolator interpolator = new FastOutSlowInInterpolator(); + public static final FastOutSlowInInterpolator interpolator = new FastOutSlowInInterpolator(); private float[] segment = new float[2]; private void updateSegment() { final long now = SystemClock.elapsedRealtime(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/CreateGroupCallBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/CreateGroupCallBottomSheet.java index b96fbcbc25d..d9f2ea2b007 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/CreateGroupCallBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/CreateGroupCallBottomSheet.java @@ -61,6 +61,7 @@ public static void show(ArrayList peers, BaseFragment fragment, long private final JoinCallAlert.JoinCallAlertDelegate joinCallDelegate; private final List chats; private final boolean needSelector; + private final boolean canRtmpStream; private final boolean isChannelOrGiga; private boolean isScheduleSelected; private TLRPC.Peer selectedPeer; @@ -79,6 +80,7 @@ public CreateGroupCallBottomSheet(BaseFragment fragment, ArrayList a this.isChannelOrGiga = ChatObject.isChannelOrGiga(chat); this.selectedPeer = chats.get(0); this.needSelector = chats.size() > 1; + this.canRtmpStream = ChatObject.canManageCalls(chat); Context context = containerView.getContext(); View divider = new View(context) { @@ -98,8 +100,8 @@ protected void onDraw(Canvas canvas) { startBtn.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); startBtn.setTypeface(AndroidUtilities.bold()); startBtn.setText(isChannelOrGiga - ? LocaleController.formatString("VoipChannelStartVoiceChat", R.string.VoipChannelStartVoiceChat) - : LocaleController.formatString("VoipGroupStartVoiceChat", R.string.VoipGroupStartVoiceChat) + ? LocaleController.formatString(R.string.VoipChannelStartVoiceChat) + : LocaleController.formatString(R.string.VoipGroupStartVoiceChat) ); startBtn.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); startBtn.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(8), Theme.getColor(Theme.key_featuredStickers_addButton), ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_windowBackgroundWhite), 120))); @@ -112,8 +114,8 @@ protected void onDraw(Canvas canvas) { scheduleBtn.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); scheduleBtn.setTypeface(AndroidUtilities.bold()); scheduleBtn.setText(isChannelOrGiga - ? LocaleController.formatString("VoipChannelScheduleVoiceChat", R.string.VoipChannelScheduleVoiceChat) - : LocaleController.formatString("VoipGroupScheduleVoiceChat", R.string.VoipGroupScheduleVoiceChat) + ? LocaleController.formatString(R.string.VoipChannelScheduleVoiceChat) + : LocaleController.formatString(R.string.VoipGroupScheduleVoiceChat) ); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { scheduleBtn.setLetterSpacing(0.025f); @@ -255,7 +257,7 @@ public int getItemViewType(int position) { @Override public int getItemCount() { - return needSelector ? CONTENT_VIEWS_COUNT + chats.size() : 1; + return needSelector ? CONTENT_VIEWS_COUNT + chats.size() : (canRtmpStream ? 2 : 1); } }; } @@ -275,8 +277,8 @@ public TopCell(Context context, boolean isChannelOrGiga) { TextView title = new TextView(context); title.setTypeface(AndroidUtilities.bold()); title.setText(isChannelOrGiga - ? LocaleController.formatString("StartVoipChannelTitle", R.string.StartVoipChannelTitle) - : LocaleController.formatString("StartVoipChatTitle", R.string.StartVoipChatTitle) + ? LocaleController.formatString(R.string.StartVoipChannelTitle) + : LocaleController.formatString(R.string.StartVoipChatTitle) ); title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); title.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); @@ -287,8 +289,8 @@ public TopCell(Context context, boolean isChannelOrGiga) { description.setGravity(Gravity.CENTER_HORIZONTAL); description.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); description.setText(isChannelOrGiga - ? LocaleController.formatString("VoipChannelStart2", R.string.VoipChannelStart2) - : LocaleController.formatString("VoipGroupStart2", R.string.VoipGroupStart2) + ? LocaleController.formatString(R.string.VoipChannelStart2) + : LocaleController.formatString(R.string.VoipGroupStart2) ); description.setLineSpacing(description.getLineSpacingExtra(), description.getLineSpacingMultiplier() * 1.1f); addView(description, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 28, 0, 28, 17)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/DialogsBotsAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/DialogsBotsAdapter.java index fdf6cd72dd8..4d717bfb0fd 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/DialogsBotsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/DialogsBotsAdapter.java @@ -4,27 +4,39 @@ import android.content.Context; import android.database.sqlite.SQLiteStatement; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.TextPaint; import android.text.TextUtils; +import android.text.style.ClickableSpan; +import android.text.style.URLSpan; import android.view.View; +import androidx.annotation.NonNull; + import org.telegram.SQLite.SQLiteCursor; import org.telegram.SQLite.SQLiteDatabase; import org.telegram.SQLite.SQLitePreparedStatement; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.DialogObject; import org.telegram.messenger.FileLog; +import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.MessagesStorage; import org.telegram.messenger.R; +import org.telegram.messenger.browser.Browser; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.tl.TL_bots; +import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.Theme; import java.util.ArrayList; import java.util.HashSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class DialogsBotsAdapter extends UniversalAdapter { @@ -43,8 +55,10 @@ public class DialogsBotsAdapter extends UniversalAdapter { public boolean expandedMyBots; public boolean expandedSearchBots; + private final CharSequence infoText; + public DialogsBotsAdapter(RecyclerListView listView, Context context, int currentAccount, int folderId, boolean showOnlyPopular, Theme.ResourcesProvider resourcesProvider) { - super(listView, context, currentAccount, 0, null, resourcesProvider); + super(listView, context, currentAccount, 0, true, null, resourcesProvider); super.fillItems = this::fillItems; this.context = context; this.currentAccount = currentAccount; @@ -52,6 +66,37 @@ public DialogsBotsAdapter(RecyclerListView listView, Context context, int curren this.resourcesProvider = resourcesProvider; this.showOnlyPopular = showOnlyPopular; this.popular = new PopularBots(currentAccount, () -> update(true)); + this.infoText = AndroidUtilities.replaceArrows(AndroidUtilities.replaceSingleTag(LocaleController.getString(R.string.AppsTabInfo), () -> { + final AlertDialog[] alert = new AlertDialog[1]; + SpannableStringBuilder text = AndroidUtilities.replaceTags(AndroidUtilities.replaceLinks(LocaleController.getString(R.string.AppsTabInfoText), resourcesProvider, () -> { + if (alert[0] != null) { + alert[0].dismiss(); + } + })); + Matcher m = Pattern.compile("@([a-zA-Z0-9_-]+)").matcher(text); + while (m.find()) { + final String username = m.group(1); + text.setSpan(new ClickableSpan() { + @Override + public void onClick(@NonNull View widget) { + if (alert[0] != null) { + alert[0].dismiss(); + } + Browser.openUrl(context, "https://t.me/" + username); + } + @Override + public void updateDrawState(@NonNull TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + } + }, m.start(), m.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + alert[0] = new AlertDialog.Builder(context, resourcesProvider) + .setTitle(LocaleController.getString(R.string.AppsTabInfoTitle)) + .setMessage(text) + .setPositiveButton(LocaleController.getString(R.string.AppsTabInfoButton), null) + .show(); + }), true); update(false); MediaDataController.getInstance(currentAccount).loadHints(true); } @@ -98,6 +143,7 @@ public void fillItems(ArrayList items, UniversalAdapter adapter) { top_peers_bots.add(user); } } + boolean hasAdded = false; topPeersStart = items.size(); if (!top_peers_bots.isEmpty() && !showOnlyPopular) { if (top_peers_bots.size() > 5) { @@ -121,6 +167,7 @@ public void fillItems(ArrayList items, UniversalAdapter adapter) { if (uids.contains(user.id)) continue; uids.add(user.id); items.add(UItem.asProfileCell(user).accent()); + hasAdded = true; } if (popular.loading) { items.add(UItem.asFlicker(FlickerLoadingView.PROFILE_SEARCH_CELL)); @@ -134,6 +181,9 @@ public void fillItems(ArrayList items, UniversalAdapter adapter) { items.add(UItem.asFlicker(FlickerLoadingView.PROFILE_SEARCH_CELL)); items.add(UItem.asFlicker(FlickerLoadingView.PROFILE_SEARCH_CELL)); } + if (hasAdded) { + items.add(UItem.asShadow(infoText)); + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiPacksAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiPacksAlert.java index b7b04fa85eb..ab7b057e5c3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiPacksAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiPacksAlert.java @@ -1902,7 +1902,7 @@ public void init() { }); if (data.length == 1 && stickerSet != null && stickerSet.set != null && !stickerSet.set.emojis) { AndroidUtilities.runOnUIThread(() -> EmojiPacksAlert.this.dismiss()); - StickersAlert alert = new StickersAlert(getContext(), fragment, inputStickerSets.get(i), null, fragment instanceof ChatActivity ? ((ChatActivity) fragment).getChatActivityEnterView() : null, resourcesProvider); + StickersAlert alert = new StickersAlert(getContext(), fragment, inputStickerSets.get(i), null, fragment instanceof ChatActivity ? ((ChatActivity) fragment).getChatActivityEnterView() : null, resourcesProvider, false); alert.show(); return; } @@ -1920,7 +1920,7 @@ public void didReceivedNotification(int id, int account, Object... args) { TLRPC.TL_messages_stickerSet stickerSet = MediaDataController.getInstance(currentAccount).getStickerSet(this.inputStickerSets.get(i), true); if (stickerSets.size() == 1 && stickerSet != null && stickerSet.set != null && !stickerSet.set.emojis) { EmojiPacksAlert.this.dismiss(); - StickersAlert alert = new StickersAlert(getContext(), fragment, inputStickerSets.get(i), null, fragment instanceof ChatActivity ? ((ChatActivity) fragment).getChatActivityEnterView() : null, resourcesProvider); + StickersAlert alert = new StickersAlert(getContext(), fragment, inputStickerSets.get(i), null, fragment instanceof ChatActivity ? ((ChatActivity) fragment).getChatActivityEnterView() : null, resourcesProvider, false); alert.show(); return; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiTabsStrip.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiTabsStrip.java index d57e1cc04d4..b22a680c1d9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiTabsStrip.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiTabsStrip.java @@ -30,6 +30,7 @@ import androidx.core.math.MathUtils; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ImageLocation; import org.telegram.messenger.ImageReceiver; import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MessageObject; @@ -500,6 +501,9 @@ public void updateEmojiPacks(ArrayList emojiPacks) { } else { currentPackButton.setAnimatedEmojiDocument(thumbDocument); } + if (thumbDocument == null) { + currentPackButton.setStickerThumb(newPack); + } currentPackButton.id = newPack.forGroup ? (long) "forGroup".hashCode() : null; currentPackButton.updateSelect(selected == i, false); if (currentType == SelectAnimatedEmojiDialog.TYPE_AVATAR_CONSTRUCTOR || currentType == SelectAnimatedEmojiDialog.TYPE_CHAT_REACTIONS || currentType == SelectAnimatedEmojiDialog.TYPE_SET_REPLY_ICON || currentType == SelectAnimatedEmojiDialog.TYPE_SET_REPLY_ICON_BOTTOM) { @@ -676,11 +680,12 @@ public class EmojiTabButton extends ViewGroup { public boolean keepAttached; private boolean isAnimatedEmoji; - private ImageView imageView; + private BackupImageView imageView; private RLottieDrawable lottieDrawable; private PremiumLockIconView lockView; private boolean round, forceSelector; TLRPC.Document animatedEmojiDocument; + EmojiView.EmojiPack setObject; AnimatedEmojiDrawable animatedEmoji; boolean attached; @@ -688,6 +693,9 @@ public Long id() { if (id != null) { return id; } + if (setObject != null && setObject.set != null) { + return setObject.set.id; + } if (animatedEmojiDocument != null) { return animatedEmojiDocument.id; } @@ -711,7 +719,8 @@ public EmojiTabButton(Context context, int drawableId, int lottieId, boolean rou lottieDrawable.setAllowDecodeSingleFrame(true); lottieDrawable.start(); } else { - imageView = new ImageView(context); + imageView = new BackupImageView(context); + imageView.applyAttach = false; imageView.setImageDrawable(context.getResources().getDrawable(drawableId).mutate()); addView(imageView); } @@ -728,7 +737,8 @@ public EmojiTabButton(Context context, int drawableId, boolean roundSelector, bo setBackground(Theme.createRadSelectorDrawable(selectorColor(), 8, 8)); } - imageView = new ImageView(context); + imageView = new BackupImageView(context); + imageView.applyAttach = false; imageView.setImageDrawable(context.getResources().getDrawable(drawableId).mutate()); setColor(Theme.getColor(Theme.key_chat_emojiPanelIcon, resourcesProvider)); @@ -746,7 +756,7 @@ public EmojiTabButton(Context context, TLRPC.Document emojiDocument, boolean fre setBackground(Theme.createRadSelectorDrawable(selectorColor(), 8, 8)); } - imageView = new ImageView(context) { + imageView = new BackupImageView(context) { @Override public void invalidate() { if (HwEmojis.grab(this)) { @@ -763,27 +773,8 @@ public void invalidate(int l, int t, int r, int b) { } super.invalidate(l, t, r, b); } - - @Override - protected void onDraw(Canvas canvas) { - - } - - @Override - protected void dispatchDraw(Canvas canvas) { - Drawable drawable = getDrawable(); - if (drawable != null) { - drawable.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); - drawable.setAlpha(255); - drawable.draw(canvas); - } - } - - @Override - public void setImageDrawable(@Nullable Drawable drawable) { - super.setImageDrawable(drawable); - } }; + imageView.applyAttach = false; animatedEmojiDocument = emojiDocument; isAnimatedEmoji = true; imageView.setColorFilter(getEmojiColorFilter()); @@ -863,6 +854,7 @@ public boolean performClick() { public void setDrawable(Drawable drawable) { setAnimatedEmojiDocument(null); + setStickerThumb(null); imageView.setImageDrawable(drawable); } @@ -871,13 +863,26 @@ public void setAnimatedEmojiDocument(TLRPC.Document document) { if (animatedEmoji != null) { animatedEmoji.removeView(imageView); animatedEmoji = null; - imageView.setImageDrawable(null); } + imageView.clearImage(); animatedEmojiDocument = document; updateAttachState(); } } + public void setStickerThumb(EmojiView.EmojiPack set) { + if (set != null && set.set == null) set = null; + if (setObject == null || set == null || setObject.set.id != set.set.id) { + if (animatedEmoji != null && animatedEmojiDocument == null) { + animatedEmoji.removeView(imageView); + animatedEmoji = null; + } + imageView.clearImage(); + setObject = set; + updateAttachState(); + } + } + private void playAnimation() { if (animatedEmoji != null) { ImageReceiver imageReceiver = animatedEmoji.getImageReceiver(); @@ -1041,7 +1046,7 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { } public Drawable getDrawable() { - return imageView != null ? imageView.getDrawable() : null; + return imageView != null ? imageView.getImageReceiver().getImageDrawable() : null; } @Override @@ -1062,22 +1067,37 @@ private void updateAttachState() { if (imageView == null) { return; } - if (animatedEmoji != null && animatedEmojiDocument == null) { - animatedEmoji.removeView(imageView); - animatedEmoji = null; - imageView.setImageDrawable(null); - } else if (attached && isVisible) { + if (attached && isVisible) { if (animatedEmoji == null && animatedEmojiDocument != null) { + imageView.clearImage(); animatedEmoji = AnimatedEmojiDrawable.make(UserConfig.selectedAccount, animatedEmojiCacheType, animatedEmojiDocument); animatedEmoji.addView(imageView); imageView.setImageDrawable(animatedEmoji); + } else { + if (animatedEmoji != null) { + animatedEmoji.removeView(imageView); + animatedEmoji = null; + } + imageView.clearImage(); + if (setObject != null) { + imageView.setImage(ImageLocation.getForStickerSet(setObject.set), "24_24", null, null, setObject); + if (setObject.needLoadSet != null) { + MediaDataController.getInstance(UserConfig.selectedAccount).getStickerSet(setObject.needLoadSet, false); + setObject.needLoadSet = null; + } + } } } else { if (animatedEmoji != null) { animatedEmoji.removeView(imageView); animatedEmoji = null; - imageView.setImageDrawable(null); } + imageView.clearImage(); + } + if (attached && isVisible) { + imageView.onAttachedToWindow(); + } else { + imageView.onDetachedFromWindow(); } updateLockImageReceiver(); } @@ -1087,7 +1107,7 @@ private void updateAttachState() { private ValueAnimator selectAnimator; public void updateSelect(boolean selected, boolean animated) { - if (imageView != null && imageView.getDrawable() == null) { + if (imageView != null && imageView.getImageReceiver().getImageDrawable() == null) { return; } if (this.selected == selected) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java index 61a9e3bc3b3..0ff260e62d0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java @@ -5240,7 +5240,7 @@ private void updateStickerTabs(boolean updateStickerSets) { final TLRPC.StickerSetCovered installingStickerSet = primaryInstallingStickerSets[i]; if (installingStickerSet != null) { final TLRPC.TL_messages_stickerSet pack = mediaDataController.getStickerSetById(installingStickerSet.set.id); - if (pack != null && !pack.set.archived) { + if (pack != null && pack.set != null && !pack.set.archived) { primaryInstallingStickerSets[i] = null; } else { final TLRPC.TL_messages_stickerSet set = new TLRPC.TL_messages_stickerSet(); @@ -5259,7 +5259,7 @@ private void updateStickerTabs(boolean updateStickerSets) { packs = MessagesController.getInstance(currentAccount).filterPremiumStickers(packs); for (int a = 0; a < packs.size(); a++) { TLRPC.TL_messages_stickerSet pack = packs.get(a); - if (pack.set.archived || pack.documents == null || pack.documents.isEmpty()) { + if (pack.set != null && pack.set.archived || pack.documents == null || pack.documents.isEmpty()) { continue; } stickerSets.add(pack); @@ -6772,6 +6772,7 @@ public static class EmojiPack { public int index; public TLRPC.StickerSet set; public ArrayList documents = new ArrayList<>(); + public TLRPC.InputStickerSet needLoadSet; public boolean free; public boolean installed; public boolean featured; @@ -7069,6 +7070,10 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, @SuppressLint("Recy EmojiPack pack2 = emojipacksProcessed.get(a); EmojiPack before = a - 1 >= 0 ? emojipacksProcessed.get(a - 1) : null; boolean divider = pack2 != null && pack2.featured && !(before != null && !before.free && before.installed && !UserConfig.getInstance(currentAccount).isPremium()); + if (pack2 != null && pack2.needLoadSet != null) { + MediaDataController.getInstance(currentAccount).getStickerSet(pack2.needLoadSet, false); + pack2.needLoadSet = null; + } header.setStickerSet(pack2, divider); break; } @@ -7204,15 +7209,16 @@ public void processEmoji(boolean updateEmojipacks) { // continue; // } EmojiPack pack = new EmojiPack(); - pack.installed = mediaDataController.isStickerPackInstalled(set.set.id); pack.set = set.set; if (set instanceof TLRPC.TL_stickerSetFullCovered) { pack.documents = ((TLRPC.TL_stickerSetFullCovered) set).documents; } else if (set instanceof TLRPC.TL_stickerSetNoCovered) { - TLRPC.TL_messages_stickerSet stickerSet = mediaDataController.getStickerSet(MediaDataController.getInputStickerSet(set.set), set.set.hash, false); + TLRPC.TL_messages_stickerSet stickerSet = mediaDataController.getStickerSet(MediaDataController.getInputStickerSet(set.set), set.set.hash, true); if (stickerSet != null) { pack.documents = stickerSet.documents; + } else { + pack.needLoadSet = MediaDataController.getInputStickerSet(set.set); } } else { pack.documents = set.covers; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextView.java index d090474ba27..9dec38bae38 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextView.java @@ -8,6 +8,8 @@ package org.telegram.ui.Components; +import static org.telegram.messenger.AndroidUtilities.dp; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -26,6 +28,7 @@ import android.graphics.RectF; import android.graphics.Shader; import android.graphics.Typeface; +import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; import android.os.SystemClock; @@ -49,6 +52,7 @@ import androidx.annotation.IntDef; import androidx.annotation.Keep; +import androidx.annotation.NonNull; import org.telegram.messenger.AccountInstance; import org.telegram.messenger.AndroidUtilities; @@ -153,8 +157,12 @@ public class FragmentContextView extends FrameLayout implements NotificationCent private Matrix matrix; private int gradientWidth; private TextPaint gradientTextPaint; - private StaticLayout timeLayout; - private RectF rect = new RectF(); + + private boolean notifyButtonEnabled; + private boolean willBeNotified; + private AnimatedTextView.AnimatedTextDrawable notifyText = new AnimatedTextView.AnimatedTextDrawable(false, true, true); + private ButtonBounce notifyButtonBounce; + private boolean scheduleRunnableScheduled; private final Runnable updateScheduleTimeRunnable = new Runnable() { @Override @@ -165,7 +173,7 @@ public void run() { } ChatObject.Call call = chatActivity.getGroupCall(); if (call == null || !call.isScheduled()) { - timeLayout = null; + notifyButtonEnabled = false; scheduleRunnableScheduled = false; return; } @@ -177,8 +185,7 @@ public void run() { } else { str = AndroidUtilities.formatFullDuration(call.call.schedule_date - currentTime); } - int width = (int) Math.ceil(gradientTextPaint.measureText(str)); - timeLayout = new StaticLayout(str, gradientTextPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + notifyText.setText(willBeNotified ? str : LocaleController.getString(R.string.VoipChatNotify), true); AndroidUtilities.runOnUIThread(updateScheduleTimeRunnable, 1000); frameLayout.invalidate(); } @@ -292,11 +299,13 @@ public void invalidate() { } } + private final RectF notifyButtonRect = new RectF(); + @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); - if (currentStyle == STYLE_INACTIVE_GROUP_CALL && timeLayout != null) { - int width = (int) Math.ceil(timeLayout.getLineWidth(0)) + AndroidUtilities.dp(24); + if (currentStyle == STYLE_INACTIVE_GROUP_CALL && notifyButtonEnabled) { + int width = (int) Math.ceil(notifyText.getCurrentWidth()) + dp(24); if (width != gradientWidth) { linearGradient = new LinearGradient(0, 0, width * 1.7f, 0, new int[]{0xff648CF4, 0xff8C69CF, 0xffD45979, 0xffD45979}, new float[]{0.0f, 0.294f, 0.588f, 1.0f}, Shader.TileMode.CLAMP); gradientPaint.setShader(linearGradient); @@ -318,18 +327,56 @@ protected void dispatchDraw(Canvas canvas) { matrix.reset(); matrix.postTranslate(-gradientWidth * 0.7f * moveProgress, 0); linearGradient.setLocalMatrix(matrix); - int x = getMeasuredWidth() - width - AndroidUtilities.dp(10); - int y = AndroidUtilities.dp(10); - rect.set(0, 0, width, AndroidUtilities.dp(28)); + int x = getMeasuredWidth() - width - dp(10); + int y = dp(10); + notifyButtonRect.set(x, y, x + width, y + dp(28)); canvas.save(); + final float s = notifyButtonBounce.getScale(0.1f); + canvas.scale(s, s, notifyButtonRect.centerX(), notifyButtonRect.centerY()); canvas.translate(x, y); - canvas.drawRoundRect(rect, AndroidUtilities.dp(16), AndroidUtilities.dp(16), gradientPaint); - canvas.translate(AndroidUtilities.dp(12), AndroidUtilities.dp(6)); - timeLayout.draw(canvas); + AndroidUtilities.rectTmp.set(0, 0, width, dp(28)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(16), dp(16), gradientPaint); + canvas.translate(dp(12), dp(6)); + notifyText.setBounds(0, 0, AndroidUtilities.displaySize.x, dp(16)); + notifyText.draw(canvas); canvas.restore(); } } + + @Override + public boolean dispatchTouchEvent(MotionEvent e) { + if (currentStyle == STYLE_INACTIVE_GROUP_CALL && notifyButtonEnabled && notifyButtonBounce != null) { + final boolean hit = notifyButtonRect.contains(e.getX(), e.getY()); + if (e.getAction() == MotionEvent.ACTION_DOWN) { + notifyButtonBounce.setPressed(hit); + } else if (e.getAction() == MotionEvent.ACTION_MOVE) { + if (!hit) notifyButtonBounce.setPressed(false); + } else if (e.getAction() == MotionEvent.ACTION_UP) { + if (hit) { + toggleScheduledNotify(); + } + notifyButtonBounce.setPressed(false); + } else if (e.getAction() == MotionEvent.ACTION_CANCEL) { + notifyButtonBounce.setPressed(false); + } + } else if (notifyButtonBounce != null) { + notifyButtonBounce.setPressed(false); + } + return (notifyButtonBounce != null && notifyButtonBounce.isPressed()) || super.dispatchTouchEvent(e); + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return who == notifyText || super.verifyDrawable(who); + } }; + notifyButtonBounce = new ButtonBounce(frameLayout); + notifyText.setOverrideFullWidth(AndroidUtilities.displaySize.x); + notifyText.setScaleProperty(.4f); + notifyText.setCallback(frameLayout); + notifyText.setTextColor(0xFFFFFFFF); + notifyText.setTextSize(dp(14)); + notifyText.setTypeface(AndroidUtilities.bold()); addView(frameLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 36, Gravity.TOP | Gravity.LEFT, 0, 0, 0, 0)); selector = new View(context); @@ -344,7 +391,7 @@ protected void dispatchDraw(Canvas canvas) { playButton.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_inappPlayerPlayPause), PorterDuff.Mode.MULTIPLY)); playButton.setImageDrawable(playPauseDrawable = new PlayPauseDrawable(14)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - playButton.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_inappPlayerPlayPause) & 0x19ffffff, 1, AndroidUtilities.dp(14))); + playButton.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_inappPlayerPlayPause) & 0x19ffffff, 1, dp(14))); } addView(playButton, LayoutHelper.createFrame(36, 36, Gravity.TOP | Gravity.LEFT)); playButton.setOnClickListener(v -> { @@ -361,7 +408,7 @@ protected void dispatchDraw(Canvas canvas) { importingImageView.setScaleType(ImageView.ScaleType.CENTER); importingImageView.setAutoRepeat(true); importingImageView.setAnimation(R.raw.import_progress, 30, 30); - importingImageView.setBackground(Theme.createCircleDrawable(AndroidUtilities.dp(22), getThemedColor(Theme.key_inappPlayerPlayPause))); + importingImageView.setBackground(Theme.createCircleDrawable(dp(22), getThemedColor(Theme.key_inappPlayerPlayPause))); addView(importingImageView, LayoutHelper.createFrame(22, 22, Gravity.TOP | Gravity.LEFT, 7, 7, 0, 0)); titleTextView = new AudioPlayerAlert.ClippingTextViewSwitcher(context) { @@ -418,9 +465,9 @@ protected TextView createTextView() { public void draw(Canvas canvas) { super.draw(canvas); - final int halfOutlineWidth = AndroidUtilities.dp(1); + final int halfOutlineWidth = dp(1); AndroidUtilities.rectTmp.set(halfOutlineWidth, halfOutlineWidth, getWidth() - halfOutlineWidth, getHeight() - halfOutlineWidth); - joinButtonFlicker.draw(canvas, AndroidUtilities.rectTmp, AndroidUtilities.dp(16), this); + joinButtonFlicker.draw(canvas, AndroidUtilities.rectTmp, dp(16), this); } @Override @@ -459,11 +506,11 @@ private void updateJoinButtonWidth(int width) { }; joinButton.setText(LocaleController.getString(R.string.VoipChatJoin)); joinButton.setTextColor(getThemedColor(Theme.key_featuredStickers_buttonText)); - joinButton.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(16), getThemedColor(Theme.key_featuredStickers_addButton), getThemedColor(Theme.key_featuredStickers_addButtonPressed))); + joinButton.setBackground(Theme.createSimpleSelectorRoundRectDrawable(dp(16), getThemedColor(Theme.key_featuredStickers_addButton), getThemedColor(Theme.key_featuredStickers_addButtonPressed))); joinButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); joinButton.setTypeface(AndroidUtilities.bold()); joinButton.setGravity(Gravity.CENTER); - joinButton.setPadding(AndroidUtilities.dp(14), 0, AndroidUtilities.dp(14), 0); + joinButton.setPadding(dp(14), 0, dp(14), 0); addView(joinButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 28, Gravity.TOP | Gravity.RIGHT, 0, 10, 14, 0)); joinButton.setOnClickListener(v -> FragmentContextView.this.callOnClick()); if (flickOnAttach) { @@ -476,7 +523,7 @@ private void updateJoinButtonWidth(int width) { silentButtonImage.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_inappPlayerClose), PorterDuff.Mode.MULTIPLY)); silentButton.addView(silentButtonImage, LayoutHelper.createFrame(20, 20, Gravity.CENTER)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - silentButton.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_inappPlayerClose) & 0x19ffffff, 1, AndroidUtilities.dp(14))); + silentButton.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_inappPlayerClose) & 0x19ffffff, 1, dp(14))); } silentButton.setContentDescription(LocaleController.getString(R.string.Unmute)); silentButton.setOnClickListener(e -> { @@ -490,12 +537,12 @@ private void updateJoinButtonWidth(int width) { } avatars = new AvatarsImageView(context, false); - avatars.setAvatarsTextSize(AndroidUtilities.dp(21)); + avatars.setAvatarsTextSize(dp(21)); avatars.setDelegate(() -> updateAvatars(true)); avatars.setVisibility(GONE); addView(avatars, LayoutHelper.createFrame(108, 36, Gravity.LEFT | Gravity.TOP)); - muteDrawable = new RLottieDrawable(R.raw.voice_muted, "" + R.raw.voice_muted, AndroidUtilities.dp(16), AndroidUtilities.dp(20), true, null); + muteDrawable = new RLottieDrawable(R.raw.voice_muted, "" + R.raw.voice_muted, dp(16), dp(20), true, null); muteButton = new RLottieImageView(context) { boolean scheduled; @@ -588,7 +635,7 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { }; muteButton.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_returnToCallText), PorterDuff.Mode.MULTIPLY)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - muteButton.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_inappPlayerClose) & 0x19ffffff, 1, AndroidUtilities.dp(14))); + muteButton.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_inappPlayerClose) & 0x19ffffff, 1, dp(14))); } muteButton.setAnimation(muteDrawable); muteButton.setScaleType(ImageView.ScaleType.CENTER); @@ -627,7 +674,7 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { closeButton.setImageResource(R.drawable.miniplayer_close); closeButton.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_inappPlayerClose), PorterDuff.Mode.MULTIPLY)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - closeButton.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_inappPlayerClose) & 0x19ffffff, 1, AndroidUtilities.dp(14))); + closeButton.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_inappPlayerClose) & 0x19ffffff, 1, dp(14))); } closeButton.setScaleType(ImageView.ScaleType.CENTER); addView(closeButton, LayoutHelper.createFrame(36, 36, Gravity.RIGHT | Gravity.TOP, 0, 0, 2, 0)); @@ -757,7 +804,7 @@ private void createPlaybackSpeedButton() { return; } playbackSpeedButton = new ActionBarMenuItem(getContext(), null, 0, getThemedColor(Theme.key_dialogTextBlack), resourcesProvider); - playbackSpeedButton.setAdditionalYOffset(AndroidUtilities.dp(24 + 6)); + playbackSpeedButton.setAdditionalYOffset(dp(24 + 6)); playbackSpeedButton.setLongClickEnabled(false); playbackSpeedButton.setVisibility(GONE); playbackSpeedButton.setTag(null); @@ -791,7 +838,7 @@ private void createPlaybackSpeedButton() { if (AndroidUtilities.density >= 3.0f) { playbackSpeedButton.setPadding(0, 1, 0, 0); } - playbackSpeedButton.setAdditionalXOffset(AndroidUtilities.dp(8)); + playbackSpeedButton.setAdditionalXOffset(dp(8)); addView(playbackSpeedButton, LayoutHelper.createFrame(36, 36, Gravity.TOP | Gravity.RIGHT, 0, 0, 36, 0)); playbackSpeedButton.setOnClickListener(v -> { float currentPlaybackSpeed = MediaController.getInstance().getPlaybackSpeed(isMusic); @@ -866,10 +913,10 @@ public void setVisibility(int visibility) { } } }; - speedHintView.setExtraTranslationY(AndroidUtilities.dp(-12)); + speedHintView.setExtraTranslationY(dp(-12)); speedHintView.setText(LocaleController.getString("SpeedHint")); MarginLayoutParams params = new MarginLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - params.rightMargin = AndroidUtilities.dp(3); + params.rightMargin = dp(3); ((ViewGroup) getParent()).addView(speedHintView, params); speedHintView.showForView(playbackSpeedButton, true); } @@ -877,7 +924,7 @@ public void setVisibility(int visibility) { public void onPanTranslationUpdate(float y) { if (speedHintView != null) { - speedHintView.setExtraTranslationY(AndroidUtilities.dp(64 + 8) + y); + speedHintView.setExtraTranslationY(dp(64 + 8) + y); } } @@ -910,7 +957,7 @@ public void updateColors() { speedIcon.setColor(color); } if (playbackSpeedButton != null && Build.VERSION.SDK_INT >= 21) { - playbackSpeedButton.setBackground(Theme.createSelectorDrawable(color & 0x19ffffff, 1, AndroidUtilities.dp(14))); + playbackSpeedButton.setBackground(Theme.createSelectorDrawable(color & 0x19ffffff, 1, dp(14))); } } @@ -974,7 +1021,7 @@ public void setTopPadding(float value) { View view = applyingView != null ? applyingView : fragment.getFragmentView(); int additionalPadding = 0; if (additionalContextView != null && additionalContextView.getVisibility() == VISIBLE && additionalContextView.getParent() != null) { - additionalPadding = AndroidUtilities.dp(additionalContextView.getStyleHeight()); + additionalPadding = dp(additionalContextView.getStyleHeight()); } if (view != null && getParent() != null) { view.setPadding(0, (int) (getVisibility() == View.VISIBLE ? topPadding : 0) + additionalPadding, 0, 0); @@ -1044,7 +1091,7 @@ private void updateStyle(@Style int style) { currentStyle = style; frameLayout.setWillNotDraw(currentStyle != STYLE_INACTIVE_GROUP_CALL); if (style != STYLE_INACTIVE_GROUP_CALL) { - timeLayout = null; + notifyButtonEnabled = false; } if (avatars != null) { @@ -1158,8 +1205,8 @@ private void updateStyle(@Style int style) { if (avatars.getVisibility() != GONE) { updateAvatars(false); } else { - titleTextView.setTranslationX(-AndroidUtilities.dp(36)); - subtitleTextView.setTranslationX(-AndroidUtilities.dp(36)); + titleTextView.setTranslationX(-dp(36)); + subtitleTextView.setTranslationX(-dp(36)); } closeButton.setVisibility(GONE); @@ -1215,7 +1262,7 @@ private void updateStyle(@Style int style) { joinButton.setVisibility(GONE); titleTextView.setLayoutParams(LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 0, 0, 0, 2)); - titleTextView.setPadding(AndroidUtilities.dp(112), 0, AndroidUtilities.dp(112) + joinButtonWidth, 0); + titleTextView.setPadding(dp(112), 0, dp(112) + joinButtonWidth, 0); if (playbackSpeedButton != null) { playbackSpeedButton.setVisibility(GONE); playbackSpeedButton.setTag(null); @@ -1736,9 +1783,9 @@ public void onAnimationEnd(Animator animation) { notificationsLocker.lock(); animatorSet = new AnimatorSet(); if (additionalContextView != null && additionalContextView.getVisibility() == VISIBLE) { - ((LayoutParams) getLayoutParams()).topMargin = -AndroidUtilities.dp(getStyleHeight() + additionalContextView.getStyleHeight()); + ((LayoutParams) getLayoutParams()).topMargin = -dp(getStyleHeight() + additionalContextView.getStyleHeight()); } else { - ((LayoutParams) getLayoutParams()).topMargin = -AndroidUtilities.dp(getStyleHeight()); + ((LayoutParams) getLayoutParams()).topMargin = -dp(getStyleHeight()); } if (delegate != null) { delegate.onAnimation(true, true); @@ -1788,7 +1835,7 @@ public void onAnimationEnd(Animator animation) { playbackSpeedButton.setAlpha(1.0f); playbackSpeedButton.setEnabled(true); } - titleTextView.setPadding(0, 0, AndroidUtilities.dp(44) + joinButtonWidth, 0); + titleTextView.setPadding(0, 0, dp(44) + joinButtonWidth, 0); stringBuilder = new SpannableStringBuilder(String.format("%s %s", messageObject.getMusicAuthor(), messageObject.getMusicTitle())); for (int i = 0; i < 2; i++) { @@ -1806,7 +1853,7 @@ public void onAnimationEnd(Animator animation) { if (messageObject.getDuration() >= 10 * 60) { playbackSpeedButton.setAlpha(1.0f); playbackSpeedButton.setEnabled(true); - titleTextView.setPadding(0, 0, AndroidUtilities.dp(44) + joinButtonWidth, 0); + titleTextView.setPadding(0, 0, dp(44) + joinButtonWidth, 0); updatePlaybackButton(false); } else { playbackSpeedButton.setAlpha(0.0f); @@ -1918,9 +1965,9 @@ public void onAnimationEnd(Animator animation) { notificationsLocker.lock(); animatorSet = new AnimatorSet(); if (additionalContextView != null && additionalContextView.getVisibility() == VISIBLE) { - ((LayoutParams) getLayoutParams()).topMargin = -AndroidUtilities.dp(getStyleHeight() + additionalContextView.getStyleHeight()); + ((LayoutParams) getLayoutParams()).topMargin = -dp(getStyleHeight() + additionalContextView.getStyleHeight()); } else { - ((LayoutParams) getLayoutParams()).topMargin = -AndroidUtilities.dp(getStyleHeight()); + ((LayoutParams) getLayoutParams()).topMargin = -dp(getStyleHeight()); } if (delegate != null) { delegate.onAnimation(true, true); @@ -2095,7 +2142,7 @@ public void onAnimationEnd(Animator animation) { if (gradientPaint == null) { gradientTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); gradientTextPaint.setColor(0xffffffff); - gradientTextPaint.setTextSize(AndroidUtilities.dp(14)); + gradientTextPaint.setTextSize(dp(14)); gradientTextPaint.setTypeface(AndroidUtilities.bold()); gradientPaint = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -2103,6 +2150,9 @@ public void onAnimationEnd(Animator animation) { matrix = new Matrix(); } + notifyButtonEnabled = true; + final String notifyString = LocaleController.getString(R.string.VoipChatNotify); + willBeNotified = call.call != null && call.call.schedule_start_subscribed; joinButton.setVisibility(GONE); if (!TextUtils.isEmpty(call.call.title)) { titleTextView.setText(call.call.title, false); @@ -2119,8 +2169,9 @@ public void onAnimationEnd(Animator animation) { updateScheduleTimeRunnable.run(); } } else { - timeLayout = null; + notifyButtonEnabled = false; joinButton.setVisibility(VISIBLE); + joinButton.setText(LocaleController.getString(R.string.VoipChatJoin)); if (!TextUtils.isEmpty(call.call.title)) { titleTextView.setText(call.call.title, false); } else if (call.call.rtmp_stream) { @@ -2156,9 +2207,9 @@ public void onAnimationEnd(Animator animation) { } animatorSet = new AnimatorSet(); if (additionalContextView != null && additionalContextView.getVisibility() == VISIBLE) { - ((LayoutParams) getLayoutParams()).topMargin = -AndroidUtilities.dp(getStyleHeight() + additionalContextView.getStyleHeight()); + ((LayoutParams) getLayoutParams()).topMargin = -dp(getStyleHeight() + additionalContextView.getStyleHeight()); } else { - ((LayoutParams) getLayoutParams()).topMargin = -AndroidUtilities.dp(getStyleHeight()); + ((LayoutParams) getLayoutParams()).topMargin = -dp(getStyleHeight()); } final int currentAccount = account; notificationsLocker2.lock(); @@ -2268,8 +2319,8 @@ private void updateAvatars(boolean animated) { int x = N == 0 ? 10 : (10 + 24 * (N - 1) + 32 + 10); if (animated) { int leftMargin = ((LayoutParams) titleTextView.getLayoutParams()).leftMargin; - if (AndroidUtilities.dp(x) != leftMargin) { - float dx = titleTextView.getTranslationX() + leftMargin - AndroidUtilities.dp(x); + if (dp(x) != leftMargin) { + float dx = titleTextView.getTranslationX() + leftMargin - dp(x); titleTextView.setTranslationX(dx); subtitleTextView.setTranslationX(dx); titleTextView.animate().translationX(0).setDuration(220).setInterpolator(CubicBezierInterpolator.DEFAULT); @@ -2314,14 +2365,14 @@ protected void dispatchDraw(Canvas canvas) { // boolean mutedByAdmin = GroupCallActivity.groupCallInstance == null && Theme.getFragmentContextViewWavesDrawable().getState() == FragmentContextViewWavesDrawable.MUTE_BUTTON_STATE_MUTED_BY_ADMIN; Theme.getFragmentContextViewWavesDrawable().updateState(wasDraw); - float progress = topPadding / AndroidUtilities.dp((getStyleHeight())); + float progress = topPadding / dp((getStyleHeight())); if (collapseTransition) { - Theme.getFragmentContextViewWavesDrawable().draw(0, AndroidUtilities.dp((getStyleHeight())) - topPadding + extraHeight, getMeasuredWidth(), getMeasuredHeight() - AndroidUtilities.dp(2), canvas, null, Math.min(progress, (1f - collapseProgress))); + Theme.getFragmentContextViewWavesDrawable().draw(0, dp((getStyleHeight())) - topPadding + extraHeight, getMeasuredWidth(), getMeasuredHeight() - dp(2), canvas, null, Math.min(progress, (1f - collapseProgress))); } else { - Theme.getFragmentContextViewWavesDrawable().draw(0, AndroidUtilities.dp((getStyleHeight())) - topPadding, getMeasuredWidth(), getMeasuredHeight() - AndroidUtilities.dp(2), canvas, this, progress); + Theme.getFragmentContextViewWavesDrawable().draw(0, dp((getStyleHeight())) - topPadding, getMeasuredWidth(), getMeasuredHeight() - dp(2), canvas, this, progress); } - float clipTop = AndroidUtilities.dp((getStyleHeight())) - topPadding; + float clipTop = dp((getStyleHeight())) - topPadding; if (collapseTransition) { clipTop += extraHeight; } @@ -2373,10 +2424,10 @@ public void setVisibility(int visibility) { private void updatePaddings() { int margin = 0; if (getVisibility() == VISIBLE) { - margin -= AndroidUtilities.dp(getStyleHeight()); + margin -= dp(getStyleHeight()); } if (additionalContextView != null && additionalContextView.getVisibility() == VISIBLE) { - margin -= AndroidUtilities.dp(additionalContextView.getStyleHeight()); + margin -= dp(additionalContextView.getStyleHeight()); ((LayoutParams) getLayoutParams()).topMargin = margin; ((LayoutParams) additionalContextView.getLayoutParams()).topMargin = margin; } else { @@ -2438,4 +2489,30 @@ private int getTitleTextColor() { private int getThemedColor(int key) { return Theme.getColor(key, resourcesProvider); } + + private int toggleGroupCallStartSubscriptionReqId = 0; + public void toggleScheduledNotify() { + if (fragment == null || chatActivity == null) return; + final ChatObject.Call call = chatActivity.getGroupCall(); + if (call == null || call.call == null) return; + + if (toggleGroupCallStartSubscriptionReqId != 0) { + fragment.getConnectionsManager().cancelRequest(toggleGroupCallStartSubscriptionReqId, true); + toggleGroupCallStartSubscriptionReqId = 0; + } + TLRPC.TL_phone_toggleGroupCallStartSubscription req = new TLRPC.TL_phone_toggleGroupCallStartSubscription(); + req.call = call.getInputGroupCall(); + call.call.schedule_start_subscribed = willBeNotified = !willBeNotified; + req.subscribed = willBeNotified; + toggleGroupCallStartSubscriptionReqId = fragment.getConnectionsManager().sendRequest(req, null); + + + if (scheduleRunnableScheduled) { + AndroidUtilities.cancelRunOnUIThread(updateScheduleTimeRunnable); + scheduleRunnableScheduled = false; + } + updateScheduleTimeRunnable.run(); + + BulletinFactory.of(fragment).createSimpleBulletin(willBeNotified ? R.raw.silent_unmute : R.raw.silent_mute, LocaleController.getString(willBeNotified ? R.string.LiveStreamWillNotify : R.string.LiveStreamWillNotNotify)).show(); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCreateSpan.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCreateSpan.java index 3e663b5177c..4076fe3d8bd 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCreateSpan.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCreateSpan.java @@ -151,6 +151,11 @@ public GroupCreateSpan(Context context, Object object, ContactsController.Contac avatarDrawable.setColor(Theme.getColor(Theme.key_premiumGradientBackground2, resourcesProvider)); firstName = LocaleController.getString(R.string.PrivacyPremium); break; + case "miniapps": + isFlag = true; + avatarDrawable.setColor(Theme.getColor(Theme.key_avatar_backgroundBlue, resourcesProvider), Theme.getColor(Theme.key_avatar_background2Blue, resourcesProvider)); + firstName = LocaleController.getString(R.string.PrivacyMiniapps); + break; case "archived": default: avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_FILTER_ARCHIVED); @@ -238,6 +243,8 @@ public GroupCreateSpan(Context context, Object object, ContactsController.Contac } if (object instanceof String && "premium".equals((String) object)) { imageReceiver.setImageBitmap(GroupCreateUserCell.makePremiumUsersDrawable(getContext(), true)); + } else if (object instanceof String && "miniapps".equals((String) object)) { + imageReceiver.setImageBitmap(GroupCreateUserCell.makeMiniAppsDrawable(getContext(), true)); } else { imageReceiver.setImage(imageLocation, "50_50", avatarDrawable, 0, null, imageParent, 1); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/HashtagActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/HashtagActivity.java new file mode 100644 index 00000000000..0952a819147 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/HashtagActivity.java @@ -0,0 +1,409 @@ +package org.telegram.ui.Components; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.SparseArray; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.core.graphics.ColorUtils; + +import org.checkerframework.checker.units.qual.A; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.HashtagSearchController; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Adapters.MessagesSearchAdapter; +import org.telegram.ui.ChatActivity; +import org.telegram.ui.ChatActivityContainer; +import org.telegram.ui.Stories.StoriesController; + +public class HashtagActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { + + private final String query; + private final String hashtag; + private final String username; + + private final StoriesController.SearchStoriesList storiesList; + + public HashtagActivity(String query) { + this(query, null); + } + public HashtagActivity(String query, Theme.ResourcesProvider resourcesProvider) { + super(); + setResourceProvider(resourcesProvider); + + if (query == null) { + query = ""; + } + query = query.trim(); + if (!query.startsWith("#") && !query.startsWith("$")) + query = "#" + query; + int atIndex = query.indexOf("@"); + if (atIndex > 0) { + hashtag = query.substring(0, atIndex); + username = query.substring(atIndex + 1); + } else { + hashtag = query; + username = null; + } + this.query = hashtag + (!TextUtils.isEmpty(username) ? "@" + username : ""); + + storiesList = new StoriesController.SearchStoriesList(currentAccount, username, hashtag); + } + + @Override + public boolean onFragmentCreate() { + getMessagesController().getStoriesController().attachedSearchLists.add(storiesList); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.storiesListUpdated); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.hashtagSearchUpdated); + storiesList.load(true, 18); + return super.onFragmentCreate(); + } + + @Override + public void onFragmentDestroy() { + getMessagesController().getStoriesController().attachedSearchLists.remove(storiesList); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.storiesListUpdated); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.hashtagSearchUpdated); + super.onFragmentDestroy(); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.storiesListUpdated) { + if (args[0] == storiesList) { + if (storiesView != null) { + updateStoriesVisible(storiesView.set(storiesList), true); + } + if (storiesTotalTextView != null) { + storiesTotalTextView.setText(LocaleController.formatPluralString("FoundStories", storiesList.getCount())); + } + } + } else if (id == NotificationCenter.hashtagSearchUpdated) { + if (chatContainer == null || chatContainer.chatActivity == null) return; + int guid = (Integer) args[0]; + if (guid != chatContainer.chatActivity.getClassGuid()) { + return; + } + + int count = (Integer) args[1]; + if (storiesView != null) { + storiesView.setMessages(count, hashtag, username); + } + } + } + + private FrameLayout contentView; + private ChatActivityContainer chatContainer; + private FrameLayout sharedMediaLayoutContainer; + private SharedMediaLayout sharedMediaLayout; + private MessagesSearchAdapter.StoriesView storiesView; + private FrameLayout storiesTotal; + private TextView storiesTotalTextView; + + + @Override + public View createView(Context context) { + actionBar.setBackButtonImage(R.drawable.ic_ab_back); + actionBar.setAllowOverlayTitle(true); + actionBar.setTitle(query); + actionBar.setBackgroundColor(getThemedColor(Theme.key_windowBackgroundWhite)); + actionBar.setItemsColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText), false); + actionBar.setItemsBackgroundColor(getThemedColor(Theme.key_actionBarWhiteSelector), false); + actionBar.setTitleColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); + actionBar.setCastShadows(true); + actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { + @Override + public void onItemClick(int id) { + if (id == -1) { + finishFragment(); + } + } + }); + + fragmentView = new FrameLayout(context); + FrameLayout frameLayout = (FrameLayout) fragmentView; + frameLayout.setBackgroundColor(getThemedColor(Theme.key_windowBackgroundWhite)); + + contentView = new FrameLayout(context) { + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + setPadding(0, 0, (int) translationY, 0); + } + }; + frameLayout.addView(contentView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + + HashtagSearchController.getInstance(currentAccount).clearSearchResults(ChatActivity.SEARCH_CHANNEL_POSTS); + Bundle args = new Bundle(); + args.putInt("chatMode", ChatActivity.MODE_SEARCH); + args.putInt("searchType", ChatActivity.SEARCH_CHANNEL_POSTS); + args.putString("searchHashtag", query); + chatContainer = new ChatActivityContainer(context, getParentLayout(), args) { + boolean activityCreated = false; + @Override + protected void initChatActivity() { + if (!activityCreated) { + activityCreated = true; + super.initChatActivity(); + } + } + }; + + contentView.addView(chatContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + + sharedMediaLayout = new SharedMediaLayout(context, 0, new SharedMediaLayout.SharedMediaPreloader(null), 0, null, null, null, SharedMediaLayout.TAB_STORIES, this, new SharedMediaLayout.Delegate() { + @Override + public void scrollToSharedMedia() {} + @Override + public boolean onMemberClick(TLRPC.ChatParticipant participant, boolean b, boolean resultOnly, View vi) { + return false; + } + @Override + public TLRPC.Chat getCurrentChat() { + return null; + } + @Override + public boolean isFragmentOpened() { + return true; + } + @Override + public RecyclerListView getListView() { + return null; + } + @Override + public boolean canSearchMembers() { + return false; + } + @Override + public void updateSelectedMediaTabText() {} + }, SharedMediaLayout.VIEW_TYPE_MEDIA_ACTIVITY, resourceProvider) { + @Override + public String getStoriesHashtag() { + return hashtag; + } + @Override + public String getStoriesHashtagUsername() { + return username; + } + @Override + protected boolean canShowSearchItem() { + return false; + } + @Override + protected void onSearchStateChanged(boolean expanded) {} + @Override + protected void drawBackgroundWithBlur(Canvas canvas, float y, Rect rectTmp2, Paint backgroundPaint) {} + @Override + protected void invalidateBlur() {} + @Override + protected boolean isStoriesView() { + return false; + } + protected boolean customTabs() { + return true; + } + @Override + protected boolean includeStories() { + return false; + } + @Override + protected boolean includeSavedDialogs() { + return false; + } + @Override + protected boolean isArchivedOnlyStoriesView() { + return false; + } + @Override + protected int getInitialTab() { + return SharedMediaLayout.TAB_STORIES; + } + @Override + protected void showActionMode(boolean show) {} + @Override + protected void onActionModeSelectedUpdate(SparseArray messageObjects) {} + @Override + protected void onTabProgress(float progress) {} + @Override + protected void onTabScroll(boolean scrolling) {} + @Override + public boolean isSearchingStories() { + return true; + } + @Override + public boolean addActionButtons() { + return false; + } + }; + if (sharedMediaLayout.getSearchOptionsItem() != null) { + sharedMediaLayout.getSearchOptionsItem().setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourceProvider), PorterDuff.Mode.MULTIPLY)); + } + sharedMediaLayout.setPinnedToTop(true); + sharedMediaLayout.photoVideoOptionsItem.setTranslationY(0); + if (sharedMediaLayout.getSearchOptionsItem() != null) { + sharedMediaLayout.getSearchOptionsItem().setTranslationY(0); + } + sharedMediaLayout.setBackgroundColor(getThemedColor(Theme.key_windowBackgroundWhite)); + sharedMediaLayout.updateStoriesList(storiesList); + sharedMediaLayoutContainer = new FrameLayout(context); + sharedMediaLayoutContainer.setBackgroundColor(getThemedColor(Theme.key_windowBackgroundWhite)); + sharedMediaLayoutContainer.addView(sharedMediaLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL, 0, 0, 0, 49)); + storiesTotal = new FrameLayout(context); + storiesTotal.setBackgroundColor(getThemedColor(Theme.key_windowBackgroundWhite)); + storiesTotalTextView = new TextView(context); + storiesTotalTextView.setTypeface(AndroidUtilities.bold()); + storiesTotalTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); + storiesTotalTextView.setTextColor(getThemedColor(Theme.key_chat_searchPanelText)); + storiesTotalTextView.setText(LocaleController.formatPluralString("FoundStories", storiesList.getCount())); + storiesTotal.addView(storiesTotalTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL | Gravity.LEFT, 18, 0, 18, 0)); + View shadow = new View(context); + shadow.setBackgroundColor(Theme.getColor(Theme.key_divider, resourceProvider)); + storiesTotal.addView(shadow, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 1.0f / AndroidUtilities.density, Gravity.FILL_HORIZONTAL | Gravity.TOP)); + sharedMediaLayoutContainer.addView(storiesTotal, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 49, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL)); + contentView.addView(sharedMediaLayoutContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + + storiesView = new MessagesSearchAdapter.StoriesView(context, resourceProvider); + storiesView.setBackground(Theme.createSelectorWithBackgroundDrawable(getThemedColor(Theme.key_windowBackgroundWhite), getThemedColor(Theme.key_listSelector))); + storiesView.setOnClickListener(v -> { + transit(!storiesVisible, true); + storiesView.transition(storiesVisible); + }); + updateStoriesVisible(storiesView.set(storiesList), false); + storiesView.setMessages(HashtagSearchController.getInstance(currentAccount).getCount(ChatActivity.SEARCH_CHANNEL_POSTS), hashtag, username); + frameLayout.addView(storiesView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.FILL_HORIZONTAL | Gravity.TOP)); + + transit(false, false); + + return fragmentView; + } + + private float contentViewValue; + private ValueAnimator contentViewAnimator; + private void updateStoriesVisible(boolean visible, boolean animated) { + storiesView.animate().cancel(); + if (contentViewAnimator != null) { + contentViewAnimator.cancel(); + } + if (!animated) { + storiesView.setVisibility(visible ? View.VISIBLE : View.GONE); + storiesView.setTranslationY(visible ? 0 : -dp(48)); + contentView.setTranslationY(visible ? dp(48) : 0); + contentView.setPadding(0, 0, 0, visible ? dp(48) : 0); + return; + } + storiesView.setVisibility(View.VISIBLE); + storiesView.animate().translationY(visible ? 0 : -dp(48)).withEndAction(() -> { + if (!visible) { + storiesView.setVisibility(View.GONE); + } + }).setDuration(320).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); + contentViewAnimator = ValueAnimator.ofFloat(contentViewValue, visible ? 1.0f : 0.0f); + contentViewAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(@NonNull ValueAnimator animation) { + contentViewValue = (float) animation.getAnimatedValue(); + contentView.setTranslationY(contentViewValue * dp(48)); + contentView.setPadding(0, 0, 0, (int) (contentViewValue * dp(48))); + } + }); + contentViewAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + contentViewValue = visible ? 1.0f : 0.0f; + contentView.setTranslationY(contentViewValue * dp(48)); + contentView.setPadding(0, 0, 0, (int) (contentViewValue * dp(48))); + } + }); + contentViewAnimator.setDuration(320); + contentViewAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + contentViewAnimator.start(); + } + + private boolean storiesVisible; + private float transitValue; + private ValueAnimator transitAnimator; + private void transit(boolean stories, boolean animated) { + if (transitAnimator != null) { + transitAnimator.cancel(); + } + if (!animated) { + storiesVisible = stories; + transitValue = stories ? 1.0f : 0.0f; + sharedMediaLayout.setScaleX(stories ? 1.0f : 0.95f); + sharedMediaLayout.setScaleY(stories ? 1.0f : 0.95f); + sharedMediaLayoutContainer.setAlpha(stories ? 1.0f : 0.0f); + sharedMediaLayoutContainer.setVisibility(stories ? View.VISIBLE : View.GONE); + if (chatContainer != null && chatContainer.chatActivity != null && chatContainer.chatActivity.messagesSearchListView != null) { + chatContainer.chatActivity.messagesSearchListView.setScaleX(AndroidUtilities.lerp(1.0f, 0.95f, transitValue)); + chatContainer.chatActivity.messagesSearchListView.setScaleY(AndroidUtilities.lerp(1.0f, 0.95f, transitValue)); + } + return; + } + if (storiesVisible == stories) return; + storiesVisible = stories; + sharedMediaLayoutContainer.setVisibility(View.VISIBLE); + transitAnimator = ValueAnimator.ofFloat(transitValue, stories ? 1.0f : 0.0f); + transitAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(@NonNull ValueAnimator animation) { + transitValue = (float) animation.getAnimatedValue(); + sharedMediaLayout.setScaleX(AndroidUtilities.lerp(0.95f, 1.0f, transitValue)); + sharedMediaLayout.setScaleY(AndroidUtilities.lerp(0.95f, 1.0f, transitValue)); + if (chatContainer != null && chatContainer.chatActivity != null && chatContainer.chatActivity.messagesSearchListView != null) { + chatContainer.chatActivity.messagesSearchListView.setScaleX(AndroidUtilities.lerp(1.0f, 0.95f, transitValue)); + chatContainer.chatActivity.messagesSearchListView.setScaleY(AndroidUtilities.lerp(1.0f, 0.95f, transitValue)); + } + sharedMediaLayoutContainer.setAlpha(transitValue); + } + }); + transitAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + transitValue = stories ? 1.0f : 0.0f; + sharedMediaLayout.setScaleX(AndroidUtilities.lerp(0.95f, 1.0f, transitValue)); + sharedMediaLayout.setScaleY(AndroidUtilities.lerp(0.95f, 1.0f, transitValue)); + if (chatContainer != null && chatContainer.chatActivity != null && chatContainer.chatActivity.messagesSearchListView != null) { + chatContainer.chatActivity.messagesSearchListView.setScaleX(AndroidUtilities.lerp(1.0f, 0.95f, transitValue)); + chatContainer.chatActivity.messagesSearchListView.setScaleY(AndroidUtilities.lerp(1.0f, 0.95f, transitValue)); + } + sharedMediaLayoutContainer.setAlpha(transitValue); + if (!stories) { + sharedMediaLayoutContainer.setVisibility(View.GONE); + } + } + }); + transitAnimator.setDuration(320); + transitAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + transitAnimator.start(); + } + + @Override + public boolean isLightStatusBar() { + int color = Theme.getColor(Theme.key_windowBackgroundWhite, null, true); + return ColorUtils.calculateLuminance(color) > 0.7f; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/HashtagsSearchAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/HashtagsSearchAdapter.java new file mode 100644 index 00000000000..0009b9eb94b --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/HashtagsSearchAdapter.java @@ -0,0 +1,203 @@ +package org.telegram.ui.Components; + +import android.content.Context; +import android.text.TextUtils; +import android.view.View; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesController; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Adapters.MessagesSearchAdapter; +import org.telegram.ui.Stories.StoriesController; + +import java.util.ArrayList; + +public class HashtagsSearchAdapter extends UniversalAdapter { + + private final int currentAccount; + private final ArrayList messages = new ArrayList<>(); + public boolean hasList; + public StoriesController.SearchStoriesList list; + + public HashtagsSearchAdapter(RecyclerListView listView, Context context, int currentAccount, int folderId, Theme.ResourcesProvider resourcesProvider) { + super(listView, context, currentAccount, 0, null, resourcesProvider); + super.fillItems = this::fillItems; + this.currentAccount = currentAccount; + } + + private boolean hadStories; + public void fillItems(ArrayList items, UniversalAdapter adapter) { + boolean hasStories = hasList && list != null && list.getLoadedCount() > 0; + if (hasStories) { + items.add(MessagesSearchAdapter.StoriesView.Factory.asStoriesList(list)); + } + hadStories = hasStories; + for (int i = 0; i < messages.size(); ++i) { + items.add(UItem.asSearchMessage(1 + i, messages.get(i))); + } + if (loading || !endReached) { + items.add(UItem.asFlicker(-2, FlickerLoadingView.DIALOG_TYPE)); + items.add(UItem.asFlicker(-3, FlickerLoadingView.DIALOG_TYPE)); + items.add(UItem.asFlicker(-4, FlickerLoadingView.DIALOG_TYPE)); + } + if (!hadStories && hasStories) { + AndroidUtilities.runOnUIThread(() -> { + scrollToTop(true); + }); + } + } + + protected boolean loading; + private int searchId = 0; + private int reqId = -1; + private boolean endReached; + private int totalCount; + private String lastQuery; + private String hashtagQuery; + private int lastRate; + private Runnable searchRunnable; + + public void setInitialData(String hashtag, ArrayList messages, int messagesLastRate, int totalCount) { + if (TextUtils.equals(hashtag, hashtagQuery)) return; + cancel(); + this.messages.clear(); + this.messages.addAll(messages); + this.totalCount = totalCount; + endReached = totalCount > messages.size(); + this.lastRate = messagesLastRate; + hashtagQuery = hashtag; + update(true); + } + + private final boolean[] cashtag = new boolean[1]; + + public void search(String query) { + lastQuery = query; + final String hashtag = getHashtag(query, cashtag); + if (!TextUtils.equals(this.hashtagQuery, hashtag)) { + messages.clear(); + endReached = false; + totalCount = 0; + cancel(); + } else if (loading) { + return; + } + final int id = ++searchId; + if (hashtag == null) return; + loading = true; + update(true); + AndroidUtilities.runOnUIThread(searchRunnable = () -> { + if (id != searchId) { + return; + } + final String finalQuery = (cashtag[0] ? "$" : "#") + hashtagQuery; + if (list == null || !TextUtils.equals(list.query, finalQuery)) { + list = new StoriesController.SearchStoriesList(currentAccount, null, finalQuery); + } + if (list.getLoadedCount() <= 0) { + list.load(true, 4); + } + hasList = true; + TLRPC.TL_channels_searchPosts req = new TLRPC.TL_channels_searchPosts(); + req.hashtag = this.hashtagQuery = hashtag; + req.limit = 10; + if (!messages.isEmpty()) { + MessageObject lastMessage = messages.get(messages.size() - 1); + req.offset_rate = lastRate; + req.offset_peer = MessagesController.getInstance(currentAccount).getInputPeer(lastMessage.messageOwner.peer_id); + } else { + req.offset_peer = new TLRPC.TL_inputPeerEmpty(); + } + reqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (id != searchId) { + return; + } + final boolean wasEmpty = messages.isEmpty(); + loading = false; + if (res instanceof TLRPC.messages_Messages) { + TLRPC.messages_Messages msgs = (TLRPC.messages_Messages) res; + if (msgs instanceof TLRPC.TL_messages_messages) { + totalCount = ((TLRPC.TL_messages_messages) msgs).messages.size(); + } else if (msgs instanceof TLRPC.TL_messages_messagesSlice) { + totalCount = ((TLRPC.TL_messages_messagesSlice) msgs).count; + } + lastRate = msgs.next_rate; + MessagesController.getInstance(currentAccount).putUsers(msgs.users, false); + MessagesController.getInstance(currentAccount).putChats(msgs.chats, false); + for (int i = 0; i < msgs.messages.size(); ++i) { + final TLRPC.Message msg = msgs.messages.get(i); + final MessageObject messageObject = new MessageObject(currentAccount, msg, false, true); + messageObject.setQuery(finalQuery); + messages.add(messageObject); + } + endReached = messages.size() >= totalCount; + checkBottom(); + } else { + endReached = true; + totalCount = messages.size(); + } + update(true); + if (wasEmpty) { + scrollToTop(false); + } + })); + }, 300); + } + + public String getHashtag(String query) { + return getHashtag(query, null); + } + + public String getHashtag(String query, final boolean[] cashtag) { + if (cashtag != null) cashtag[0] = false; + if (query == null || query.isEmpty()) return null; + String tquery = query.trim(); + if (tquery.length() <= 1) return null; + if (tquery.charAt(0) != '#' && tquery.charAt(0) != '$') return null; + if (tquery.indexOf('@') >= 0) return null; + if (cashtag != null) cashtag[0] = tquery.charAt(0) == '$'; + return tquery.substring(1); + } + + public void cancel() { + if (list != null) { + list.cancel(); + } + hasList = false; + if (reqId >= 0) { + ConnectionsManager.getInstance(currentAccount).cancelRequest(reqId, true); + reqId = -1; + } + AndroidUtilities.cancelRunOnUIThread(searchRunnable); + searchId++; + loading = false; + } + + + public void checkBottom() { + if (!TextUtils.isEmpty(lastQuery)) { + if (!endReached && !loading && seesLoading()) { + search(lastQuery); + } + } + } + + public boolean seesLoading() { + if (listView == null) return false; + for (int i = 0; i < listView.getChildCount(); ++i) { + View child = listView.getChildAt(i); + if (child instanceof FlickerLoadingView) { + return true; + } + } + return false; + } + + protected void scrollToTop(boolean ifAtTop) { + + } + +} 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 974149a8118..3c9c805a81f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ItemOptions.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ItemOptions.java @@ -42,6 +42,7 @@ import org.telegram.messenger.Utilities; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenuSubItem; import org.telegram.ui.ActionBar.ActionBarPopupWindow; import org.telegram.ui.ActionBar.BaseFragment; @@ -88,6 +89,7 @@ public static ItemOptions makeOptions(@NonNull ViewGroup container, @Nullable Th private float translateX, translateY; private int dimAlpha; + private boolean drawScrim = true; private View dimView; private ViewTreeObserver.OnPreDrawListener preDrawListener; @@ -256,6 +258,35 @@ public ItemOptions add(int iconResId, Drawable iconDrawable, CharSequence text, return this; } + public ItemOptions add(CharSequence text, CharSequence subtext, Runnable onClickListener) { + if (context == null) { + return this; + } + + ActionBarMenuSubItem subItem = new ActionBarMenuSubItem(context, false, false, resourcesProvider); + subItem.setPadding(dp(18), 0, dp(18), 0); + subItem.setText(text); + subItem.setSubtext(subtext); + + subItem.setColors(textColor != null ? textColor : Theme.getColor(Theme.key_actionBarDefaultSubmenuItem, resourcesProvider), iconColor != null ? iconColor : Theme.getColor(Theme.key_actionBarDefaultSubmenuItemIcon, resourcesProvider)); + subItem.setSelectorColor(selectorColor != null ? selectorColor : Theme.multAlpha(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem, resourcesProvider), .12f)); + + subItem.setOnClickListener(view1 -> { + if (onClickListener != null) { + onClickListener.run(); + } + dismiss(); + }); + if (minWidthDp > 0) { + subItem.setMinimumWidth(dp(minWidthDp)); + addView(subItem, LayoutHelper.createLinear(minWidthDp, LayoutHelper.WRAP_CONTENT)); + } else { + addView(subItem, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } + + return this; + } + public ItemOptions makeMultiline(boolean changeSize) { if (context == null || lastLayout.getItemsCount() <= 0) { return this; @@ -357,6 +388,23 @@ public ItemOptions addView(View view) { return this; } + public ItemOptions addFrom(ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout, ActionBar.ActionBarMenuOnItemClick clickListener) { + for (int i = 0; i < popupLayout.getItemsCount(); ++i) { + View child = popupLayout.getItemAt(i); + if (child.getVisibility() != View.VISIBLE) continue; + if (child instanceof ActionBarMenuSubItem) { + ActionBarMenuSubItem item = (ActionBarMenuSubItem) child; + final int id = (Integer) item.getTag(); + add(item.getIconResId(), item.getTextView().getText(), () -> { + if (clickListener != null) { + clickListener.onItemClick(id); + } + }); + } + } + return this; + } + public ItemOptions addView(View view, LinearLayout.LayoutParams lp) { if (view == null) { return this; @@ -465,12 +513,23 @@ public ItemOptions setDimAlpha(int dimAlpha) { return this; } + public ItemOptions setDrawScrim(boolean draw) { + this.drawScrim = draw; + return this; + } + private boolean forceTop; public ItemOptions forceTop(boolean force) { forceTop = force; return this; } + private boolean forceBottom; + public ItemOptions forceBottom(boolean force) { + forceBottom = force; + return this; + } + private int maxHeight; public ItemOptions setMaxHeight(int px) { this.maxHeight = px; @@ -518,6 +577,8 @@ public ItemOptions setBlurBackground(BlurringShader.BlurManager blurManager, flo } public int getItemsCount() { + if (lastLayout == null && layout == null) + return 0; if (lastLayout == layout) { return lastLayout.getItemsCount(); } else { @@ -657,7 +718,9 @@ public void onDismiss() { X = (container.getWidth() - layout.getMeasuredWidth()) / 2; // at the center } int Y; - if (scrimView != null) { + if (forceBottom) { + Y = (int) (Math.min(y + scrimView.getMeasuredHeight(), AndroidUtilities.displaySize.y) - layout.getMeasuredHeight() + container.getY()); + } else if (scrimView != null) { if (forceTop || y + layout.getMeasuredHeight() + dp(16) > AndroidUtilities.displaySize.y - AndroidUtilities.navigationBarHeight) { // put above scrimView y -= scrimView.getMeasuredHeight(); @@ -856,7 +919,7 @@ public DimView(Context context) { } dim = ColorUtils.setAlphaComponent(0x00000000, dimAlpha); - if (scrimView instanceof UserCell && fragment instanceof ProfileActivity) { + if (drawScrim && 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); @@ -873,7 +936,8 @@ protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(dim); - if (cachedBitmap != null && scrimView.getParent() instanceof View) { + if (!drawScrim) { + } else 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); 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 3f1405f829f..b3321b7a83c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActivity.java @@ -78,6 +78,7 @@ public class MediaActivity extends BaseFragment implements SharedMediaLayout.Sha private long dialogId; private long topicId; private String hashtag; + private String username; private int storiesCount; private FrameLayout titlesContainer; private FrameLayout[] titles = new FrameLayout[2]; @@ -114,6 +115,7 @@ public boolean onFragmentCreate() { dialogId = getArguments().getLong("dialog_id"); topicId = getArguments().getLong("topic_id", 0); hashtag = getArguments().getString("hashtag", ""); + username = getArguments().getString("username", ""); storiesCount = getArguments().getInt("storiesCount", -1); int defaultTab = SharedMediaLayout.TAB_PHOTOVIDEO; if (type == TYPE_ARCHIVED_CHANNEL_STORIES) { @@ -543,6 +545,11 @@ public String getStoriesHashtag() { return hashtag; } + @Override + public String getStoriesHashtagUsername() { + return username; + } + @Override protected boolean canShowSearchItem() { return type != TYPE_STORIES && type != TYPE_ARCHIVED_CHANNEL_STORIES; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/MentionsContainerView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/MentionsContainerView.java index 731b09d03c5..0656fac1e7e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/MentionsContainerView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MentionsContainerView.java @@ -241,7 +241,7 @@ public void onContextClick(TLRPC.BotInlineResult result) { MentionsContainerView.this.onContextClick(result); } - }, resourcesProvider); + }, resourcesProvider, isStories()); paddedAdapter = new PaddedListAdapter(adapter); listView.setAdapter(paddedAdapter); @@ -611,6 +611,17 @@ public void withDelegate(Delegate delegate) { Object object = getAdapter().getItem(position); int start = getAdapter().getResultStartPosition(); int len = getAdapter().getResultLength(); + if (getAdapter().isLocalHashtagHint(position)) { + TLRPC.Chat currentChat = getAdapter().chat; + if (currentChat == null && getAdapter().parentFragment != null) { + currentChat = getAdapter().parentFragment.getCurrentChat(); + } + delegate.replaceText(start, len, getAdapter().getHashtagHint() + (currentChat != null ? "@" + ChatObject.getPublicUsername(currentChat) : "") + " ", false); + return; + } else if (getAdapter().isGlobalHashtagHint(position)) { + delegate.replaceText(start, len, getAdapter().getHashtagHint() + " ", false); + return; + } if (object instanceof TLRPC.TL_document) { MessageObject.SendAnimationData sendAnimationData = null; if (view instanceof StickerCell) { @@ -898,4 +909,8 @@ public void didReceivedNotification(int id, int account, Object... args) { } } + protected boolean isStories() { + return false; + } + } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/MessagePrivateSeenView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/MessagePrivateSeenView.java index 970e845c4d6..a1c0f01c8d0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/MessagePrivateSeenView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MessagePrivateSeenView.java @@ -45,7 +45,12 @@ public class MessagePrivateSeenView extends FrameLayout { + public static final int TYPE_SEEN = 0; + public static final int TYPE_EDIT = 1; + public static final int TYPE_FORWARD = 2; + private final int currentAccount; + private final int type; private final Theme.ResourcesProvider resourcesProvider; private final LinearLayout valueLayout; @@ -55,12 +60,15 @@ public class MessagePrivateSeenView extends FrameLayout { private final long dialogId; private final int messageId; + private final int edit_date; + private final int fwd_date; private final Runnable dismiss; private final int messageDiff; - public MessagePrivateSeenView(Context context, @NonNull MessageObject messageObject, Runnable dismiss, Theme.ResourcesProvider resourcesProvider) { + public MessagePrivateSeenView(Context context, int type, @NonNull MessageObject messageObject, Runnable dismiss, Theme.ResourcesProvider resourcesProvider) { super(context); + this.type = type; currentAccount = messageObject.currentAccount; this.resourcesProvider = resourcesProvider; @@ -69,10 +77,22 @@ public MessagePrivateSeenView(Context context, @NonNull MessageObject messageObj dialogId = messageObject.getDialogId(); messageId = messageObject.getId(); + edit_date = messageObject.messageOwner == null ? 0 : messageObject.messageOwner.edit_date; + fwd_date = messageObject.messageOwner == null || messageObject.messageOwner.fwd_from == null ? 0 : messageObject.messageOwner.fwd_from.date; ImageView iconView = new ImageView(context); addView(iconView, LayoutHelper.createFrame(24, 24, Gravity.LEFT | Gravity.CENTER_VERTICAL, 11, 0, 0, 0)); - Drawable drawable = ContextCompat.getDrawable(context, messageObject.isVoice() ? R.drawable.msg_played : R.drawable.msg_seen).mutate(); + int icon; + if (type == TYPE_EDIT) { + icon = R.drawable.menu_edited_stamp; + } else if (type == TYPE_FORWARD) { + icon = R.drawable.menu_forward_stamp; + } else if (messageObject.isVoice()) { + icon = R.drawable.msg_played; + } else { + icon = R.drawable.msg_seen; + } + Drawable drawable = ContextCompat.getDrawable(context, icon).mutate(); drawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_actionBarDefaultSubmenuItemIcon, resourcesProvider), PorterDuff.Mode.MULTIPLY)); iconView.setImageDrawable(drawable); @@ -105,6 +125,19 @@ public MessagePrivateSeenView(Context context, @NonNull MessageObject messageObj } private void request() { + if (type == TYPE_EDIT) { + valueLayout.setAlpha(1f); + loadingView.setAlpha(0f); + premiumTextView.setVisibility(View.GONE); + valueTextView.setText(LocaleController.formatPmEditedDate(edit_date)); + return; + } else if (type == TYPE_FORWARD) { + valueLayout.setAlpha(1f); + loadingView.setAlpha(0f); + premiumTextView.setVisibility(View.GONE); + valueTextView.setText(LocaleController.formatPmFwdDate(fwd_date)); + return; + } setOnClickListener(null); valueLayout.setAlpha(0f); loadingView.setAlpha(1f); @@ -310,17 +343,21 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (minWidth < 0) { minWidth = 0; - final long date = System.currentTimeMillis(); - minWidth = Math.max(minWidth, dp(40 + 96 + 8)); - minWidth = Math.max(minWidth, dp(40 + 8) + valueTextView.getPaint().measureText(LocaleController.getString(R.string.PmReadUnknown))); - minWidth = Math.max(minWidth, dp(40 + 16 + 8) + valueTextView.getPaint().measureText(LocaleController.getString(R.string.PmRead) + premiumTextView.getPaint().measureText(LocaleController.getString(R.string.PmReadShowWhen)))); - minWidth = Math.max(minWidth, dp(40 + 8) + valueTextView.getPaint().measureText(LocaleController.formatString(R.string.PmReadTodayAt, LocaleController.getInstance().getFormatterDay().format(new Date(date))))); - if (messageDiff > 60 * 60 * 24) { - minWidth = Math.max(minWidth, dp(40 + 8) + valueTextView.getPaint().measureText(LocaleController.formatString(R.string.PmReadYesterdayAt, LocaleController.getInstance().getFormatterDay().format(new Date(date))))); - } - if (messageDiff > 60 * 60 * 24 * 2) { - minWidth = Math.max(minWidth, dp(40 + 8) + valueTextView.getPaint().measureText(LocaleController.formatString(R.string.PmReadDateTimeAt, LocaleController.getInstance().getFormatterDayMonth().format(new Date(date)), LocaleController.getInstance().getFormatterDay().format(new Date(date))))); - minWidth = Math.max(minWidth, dp(40 + 8) + valueTextView.getPaint().measureText(LocaleController.formatString(R.string.PmReadDateTimeAt, LocaleController.getInstance().getFormatterYear().format(new Date(date)), LocaleController.getInstance().getFormatterDay().format(new Date(date))))); + if (type == TYPE_SEEN) { + final long date = System.currentTimeMillis(); + minWidth = Math.max(minWidth, dp(40 + 96 + 8)); + minWidth = Math.max(minWidth, dp(40 + 8) + valueTextView.getPaint().measureText(LocaleController.getString(R.string.PmReadUnknown))); + minWidth = Math.max(minWidth, dp(40 + 16 + 8) + valueTextView.getPaint().measureText(LocaleController.getString(R.string.PmRead) + premiumTextView.getPaint().measureText(LocaleController.getString(R.string.PmReadShowWhen)))); + minWidth = Math.max(minWidth, dp(40 + 8) + valueTextView.getPaint().measureText(LocaleController.formatString(R.string.PmReadTodayAt, LocaleController.getInstance().getFormatterDay().format(new Date(date))))); + if (messageDiff > 60 * 60 * 24) { + minWidth = Math.max(minWidth, dp(40 + 8) + valueTextView.getPaint().measureText(LocaleController.formatString(R.string.PmReadYesterdayAt, LocaleController.getInstance().getFormatterDay().format(new Date(date))))); + } + if (messageDiff > 60 * 60 * 24 * 2) { + minWidth = Math.max(minWidth, dp(40 + 8) + valueTextView.getPaint().measureText(LocaleController.formatString(R.string.PmReadDateTimeAt, LocaleController.getInstance().getFormatterDayMonth().format(new Date(date)), LocaleController.getInstance().getFormatterDay().format(new Date(date))))); + minWidth = Math.max(minWidth, dp(40 + 8) + valueTextView.getPaint().measureText(LocaleController.formatString(R.string.PmReadDateTimeAt, LocaleController.getInstance().getFormatterYear().format(new Date(date)), LocaleController.getInstance().getFormatterDay().format(new Date(date))))); + } + } else { + minWidth = dp(40 + 8) + valueTextView.getPaint().measureText(valueTextView.getText().toString()); } } 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 70e5cd7c6f8..4c3edf15281 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PipVideoOverlay.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PipVideoOverlay.java @@ -34,6 +34,7 @@ import android.widget.FrameLayout; import android.widget.ImageView; +import androidx.annotation.NonNull; import androidx.core.math.MathUtils; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.FloatPropertyCompat; @@ -108,6 +109,7 @@ public class PipVideoOverlay { private boolean isVisible; private VideoForwardDrawable videoForwardDrawable = new VideoForwardDrawable(false); + private SeekSpeedDrawable seekSpeedDrawable; private int mVideoWidth, mVideoHeight; private EmbedBottomSheet parentSheet; private PhotoViewer photoViewer; @@ -145,7 +147,7 @@ public class PipVideoOverlay { private boolean postedDismissControls; private Runnable dismissControlsCallback = () -> { - if (photoViewer != null && photoViewer.getVideoPlayerRewinder().rewindCount > 0) { + if (photoViewer != null && photoViewer.getVideoPlayerRewinder().rewinding) { AndroidUtilities.runOnUIThread(this.dismissControlsCallback, 1500); return; } @@ -246,9 +248,9 @@ protected void onLongClick() { } if (photoViewerWebView != null) { - photoViewer.getVideoPlayerRewinder().startRewind(photoViewerWebView, forward, photoViewer.getCurrentVideoSpeed()); + photoViewer.getVideoPlayerRewinder().startRewind(photoViewerWebView, forward, longClickStartPoint[0], photoViewer.getCurrentVideoSpeed(), seekSpeedDrawable); } else { - photoViewer.getVideoPlayerRewinder().startRewind(videoPlayer, forward, photoViewer.getCurrentVideoSpeed()); + photoViewer.getVideoPlayerRewinder().startRewind(videoPlayer, forward, longClickStartPoint[0], photoViewer.getCurrentVideoSpeed(), seekSpeedDrawable); } if (!isShowingControls) { @@ -415,13 +417,9 @@ public static View getInnerView() { } private void cancelRewind() { - if (photoViewer == null) { - return; - } - - if (photoViewer.getVideoPlayerRewinder().rewindCount > 0) { - photoViewer.getVideoPlayerRewinder().cancelRewind(); - } + if (photoViewer == null) return; + if (photoViewer.getVideoPlayerRewinder() == null) return; + photoViewer.getVideoPlayerRewinder().cancelRewind(); } public static void updatePlayButton() { @@ -876,6 +874,8 @@ public boolean dispatchTouchEvent(MotionEvent ev) { canLongClick = false; cancelRewind(); AndroidUtilities.cancelRunOnUIThread(longClickCallback); + } else if (action == MotionEvent.ACTION_MOVE && photoViewer != null && photoViewer.getVideoPlayerRewinder() != null && photoViewer.getVideoPlayerRewinder().rewinding) { + photoViewer.getVideoPlayerRewinder().setX(ev.getX()); } if (consumingChild != null) { @@ -1022,8 +1022,24 @@ protected void onDraw(Canvas canvas) { videoForwardDrawable.setBounds(getLeft(), getTop(), getRight(), getBottom()); videoForwardDrawable.draw(canvas); } + if (photoViewer != null && photoViewer.framesRewinder != null) { + canvas.save(); + canvas.translate(getLeft(), getTop()); + photoViewer.framesRewinder.draw(canvas, getRight() - getLeft(), getBottom() - getTop()); + canvas.restore(); + } + } + + @Override + protected void dispatchDraw(@NonNull Canvas canvas) { + super.dispatchDraw(canvas); + if (seekSpeedDrawable != null && seekSpeedDrawable.isShown()) { + seekSpeedDrawable.setBounds(getLeft(), getTop(), getRight(), getBottom()); + seekSpeedDrawable.draw(canvas); + } } }; + seekSpeedDrawable = new SeekSpeedDrawable(controlsView::invalidate, true, false); controlsView.setWillNotDraw(false); controlsView.setAlpha(0f); View scrim = new View(context); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/UserSelectorBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/UserSelectorBottomSheet.java index 5255fce3de7..d4b116a957d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/UserSelectorBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/UserSelectorBottomSheet.java @@ -461,7 +461,10 @@ private void next() { List options = BoostRepository.filterGiftOptions(paymentOptions, selectedUsers.size()); options = BoostRepository.filterGiftOptionsByBilling(options); if (selectedUsers.size() == 1) { - new GiftSheet(getContext(), currentAccount, selectedUsers.get(0).id, options, this::dismiss).show(); + final long userId = selectedUsers.get(0).id; + new GiftSheet(getContext(), currentAccount, userId, options, this::dismiss) + .setBirthday(birthdays != null && birthdays.contains(userId)) + .show(); return; } // PremiumPreviewGiftToUsersBottomSheet.show(selectedUsers, options); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/cells/msg/GiveawayResultsMessageCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/cells/msg/GiveawayResultsMessageCell.java index 35969bedcff..29011a4114a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/cells/msg/GiveawayResultsMessageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/cells/msg/GiveawayResultsMessageCell.java @@ -316,7 +316,7 @@ public void setMessageContent(MessageObject messageObject, int parentWidth, int Theme.key_chat_messageLinkIn, 0, () -> AndroidUtilities.runOnUIThread(() -> { if (messageObject.getDialogId() == -giveaway.channel_id) { - parentView.getDelegate().didPressReplyMessage(parentView, giveaway.launch_msg_id); + parentView.getDelegate().didPressReplyMessage(parentView, giveaway.launch_msg_id, 0, 0, false); } else { Bundle bundle = new Bundle(); bundle.putLong("chat_id", giveaway.channel_id); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/QuoteSpan.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/QuoteSpan.java index 1849ab5e749..eaf1e6e4314 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/QuoteSpan.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/QuoteSpan.java @@ -187,11 +187,11 @@ public static int putQuoteToEditable(Editable editable, int start, int end, bool final QuoteSpan quoteSpan = styleSpan.span = new QuoteSpan(true, collapsed, styleSpan); quoteSpan.start = start; quoteSpan.end = end; - editable.setSpan(quoteSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - editable.setSpan(styleSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + editable.setSpan(quoteSpan, Utilities.clamp(start, editable.length(), 0), Utilities.clamp(end, editable.length(), 0), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + editable.setSpan(styleSpan, Utilities.clamp(start, editable.length(), 0), Utilities.clamp(end, editable.length(), 0), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - editable.insert(end, "\uFEFF"); - editable.delete(end, end + 1); + editable.insert(Utilities.clamp(end, editable.length(), 0), "\uFEFF"); + editable.delete(Utilities.clamp(end, editable.length(), 0), Utilities.clamp(end + 1, editable.length(), 0)); return selectEnd; } 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 f01a1c6f8a5..be023373ec9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieDrawable.java @@ -84,7 +84,7 @@ public class RLottieDrawable extends BitmapDrawable implements Animatable, Bitma private int finishFrame; private View currentParentView; - private ArrayList parentViews = new ArrayList<>(); + private final ArrayList parentViews = new ArrayList<>(); protected int isDice; protected int diceSwitchFramesCount = -1; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress2.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress2.java index 939e4b20309..23e1db2536e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress2.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress2.java @@ -16,6 +16,8 @@ import android.graphics.RectF; import android.view.View; +import androidx.annotation.Keep; + import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ImageLocation; import org.telegram.messenger.ImageReceiver; @@ -103,10 +105,12 @@ public void setResourcesProvider(Theme.ResourcesProvider resourcesProvider) { this.resourcesProvider = resourcesProvider; } + @Keep public void setAsMini() { mediaActionDrawable.setMini(true); } + @Keep public void setCircleRadius(int value) { circleRadius = value; overlayImageView.setRoundRadius(circleRadius); @@ -128,6 +132,7 @@ public void setBackgroundDrawable(Theme.MessageDrawable drawable) { miniMediaActionDrawable.setBackgroundDrawable(drawable); } + @Keep public void setBackgroundGradientDrawable(LinearGradient drawable) { mediaActionDrawable.setBackgroundGradientDrawable(drawable); miniMediaActionDrawable.setBackgroundGradientDrawable(drawable); @@ -162,6 +167,7 @@ public void setColorKeys(int circle, int circlePressed, int icon, int iconPresse iconPressedColorKey = iconPressed; } + @Keep public void setColors(int circle, int circlePressed, int icon, int iconPressed) { circleColor = circle; circlePressedColor = circlePressed; @@ -232,6 +238,7 @@ public int getMiniIcon() { return miniMediaActionDrawable.getCurrentIcon(); } + @Keep public void setIcon(int icon, boolean ifSame, boolean animated) { if (ifSame && icon == mediaActionDrawable.getCurrentIcon()) { return; @@ -302,6 +309,7 @@ public float getOverrideAlpha() { return overrideAlpha; } + @Keep public void draw(Canvas canvas) { if (mediaActionDrawable.getCurrentIcon() == MediaActionDrawable.ICON_NONE && mediaActionDrawable.getTransitionProgress() >= 1.0f || progressRect.isEmpty()) { return; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ChatCustomReactionsEditActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ChatCustomReactionsEditActivity.java index b1dfe856db7..859d1450cbd 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ChatCustomReactionsEditActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ChatCustomReactionsEditActivity.java @@ -15,6 +15,8 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; import android.os.Build; import android.text.Editable; @@ -27,6 +29,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ScrollView; @@ -43,19 +46,26 @@ import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.tl.TL_stories; import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.AdjustPanLayoutHelper; import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.INavigationLayout; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Cells.HeaderCell; import org.telegram.ui.Cells.SlideIntChooseView; import org.telegram.ui.Cells.TextCheckCell; import org.telegram.ui.Cells.TextInfoPrivacyCell; +import org.telegram.ui.ChannelAdminLogActivity; import org.telegram.ui.Components.AnimatedEmojiDrawable; import org.telegram.ui.Components.AnimatedEmojiSpan; import org.telegram.ui.Components.BulletinFactory; import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.HideViewAfterAnimation; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.DialogsActivity; +import org.telegram.ui.LaunchActivity; import org.telegram.ui.SelectAnimatedEmojiDialog; +import org.telegram.ui.Stories.recorder.KeyboardNotifier; import java.util.ArrayList; import java.util.HashMap; @@ -80,6 +90,9 @@ public class ChatCustomReactionsEditActivity extends BaseFragment implements Not private SlideIntChooseView slideView; private TextCheckCell paidCheckCell; private UpdateReactionsButton actionButton; + private FrameLayout actionButtonContainer; + private ImageView actionButtonContainerGradient; + private int keyboardHeight; private ScrollView scrollView; private final HashMap selectedEmojisMap = new LinkedHashMap<>(); @@ -153,9 +166,60 @@ public void onItemClick(int id) { } }); - FrameLayout rootLayout = new FrameLayout(context); scrollView = new ScrollView(context); scrollView.setFillViewport(true); + FrameLayout rootLayout = new FrameLayout(context) { + final AdjustPanLayoutHelper adjustPanLayoutHelper = new AdjustPanLayoutHelper(this) { + + @Override + protected void onTransitionStart(boolean keyboardVisible, int contentHeight) { + actionButtonContainer.setVisibility(View.VISIBLE); + actionButtonContainer.animate().alpha(!keyboardVisible ? 1.0f : 0.0f).withEndAction(() -> { + if (keyboardVisible) { + actionButtonContainer.setVisibility(View.INVISIBLE); + } + }).start(); + } + + @Override + protected void onTransitionEnd() { + + } + + @Override + protected void onPanTranslationUpdate(float y, float progress, boolean keyboardVisible) { + if (getParentLayout() != null && getParentLayout().isPreviewOpenAnimationInProgress()) { + return; + } + } + + @Override + protected boolean applyTranslation() { + return false; + } + + @Override + protected boolean heightAnimationEnabled() { + INavigationLayout actionBarLayout = getParentLayout(); + if (inPreviewMode || AndroidUtilities.isTablet() || inBubbleMode || AndroidUtilities.isInMultiwindow || actionBarLayout == null) { + return false; + } + return true; + } + }; + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + adjustPanLayoutHelper.onAttach(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + adjustPanLayoutHelper.onDetach(); + } + }; contentLayout = new LinearLayout(context); contentLayout.setOrientation(LinearLayout.VERTICAL); @@ -274,6 +338,14 @@ public boolean onTextContextMenuItem(int id) { infoCell3.setBottomPadding(70); } + actionButtonContainer = new FrameLayout(context); + + actionButtonContainerGradient = new ImageView(context); + actionButtonContainerGradient.setImageResource(R.drawable.gradient_bottom); + actionButtonContainerGradient.setScaleType(ImageView.ScaleType.FIT_XY); + actionButtonContainerGradient.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundGray, resourceProvider), PorterDuff.Mode.SRC_ATOP)); + actionButtonContainer.addView(actionButtonContainerGradient, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + actionButton = new UpdateReactionsButton(context, getResourceProvider()); actionButton.setDefaultState(); actionButton.setOnClickListener(v -> { @@ -316,7 +388,8 @@ public boolean onTextContextMenuItem(int id) { }, this::finishFragment); }); rootLayout.addView(scrollView); - rootLayout.addView(actionButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM, 13, 0, 13, 13)); + rootLayout.addView(actionButtonContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 13 + 48 + 13, Gravity.BOTTOM, 0, 0, 0, 0)); + actionButtonContainer.addView(actionButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM, 13, 13, 13, 13)); rootLayout.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); bottomDialogLayout = new FrameLayout(context) { @@ -325,13 +398,12 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto super.onLayout(changed, left, top, right, bottom); if (emojiKeyboardVisible && changed) { //support screen rotation - actionButton.setTranslationY(-bottomDialogLayout.getMeasuredHeight()); + actionButtonContainer.setTranslationY(-bottomDialogLayout.getMeasuredHeight()); updateScrollViewMarginBottom(bottomDialogLayout.getMeasuredHeight()); - scrollView.fullScroll(ScrollView.FOCUS_DOWN); +// scrollView.fullScroll(ScrollView.FOCUS_DOWN); } } }; - bottomDialogLayout.setVisibility(View.INVISIBLE); rootLayout.addView(bottomDialogLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM)); @@ -594,9 +666,9 @@ private void setCheckedEnableReactionCell(int selectType, boolean paid, boolean if (selectType == SELECT_TYPE_SOME || selectType == SELECT_TYPE_ALL || paid) { switchLayout.setVisibility(View.VISIBLE); - actionButton.setVisibility(View.VISIBLE); + actionButtonContainer.setVisibility(View.VISIBLE); if (animated) { - actionButton.animate().setListener(null).cancel(); + actionButtonContainer.animate().setListener(null).cancel(); switchLayout.animate().setListener(null).cancel(); switchLayout.animate().alpha(1f).setDuration(350).setInterpolator(CubicBezierInterpolator.DEFAULT).setListener(new AnimatorListenerAdapter() { @Override @@ -604,7 +676,7 @@ public void onAnimationEnd(Animator animation) { editText.setFocusableInTouchMode(true); } }).start(); - actionButton.animate().alpha(1f).setDuration(350).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); + actionButtonContainer.animate().alpha(1f).setDuration(350).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); if (selectedEmojisMap.isEmpty()) { selectAnimatedEmojiDialog.clearSelectedDocuments(); editText.setText(""); @@ -626,12 +698,12 @@ public void onAnimationEnd(Animator animation) { } else { if (animated) { closeKeyboard(); - actionButton.animate().setListener(null).cancel(); + actionButtonContainer.animate().setListener(null).cancel(); switchLayout.animate().setListener(null).cancel(); - actionButton.animate().alpha(0f).setDuration(350).setInterpolator(CubicBezierInterpolator.DEFAULT).setListener(new AnimatorListenerAdapter() { + actionButtonContainer.animate().alpha(0f).setDuration(350).setInterpolator(CubicBezierInterpolator.DEFAULT).setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - actionButton.setVisibility(View.INVISIBLE); + actionButtonContainer.setVisibility(View.INVISIBLE); } }).start(); switchLayout.animate().alpha(0f).setDuration(350).setInterpolator(CubicBezierInterpolator.DEFAULT).setListener(new AnimatorListenerAdapter() { @@ -643,7 +715,7 @@ public void onAnimationEnd(Animator animation) { }).start(); } else { switchLayout.setVisibility(View.INVISIBLE); - actionButton.setVisibility(View.INVISIBLE); + actionButtonContainer.setVisibility(View.INVISIBLE); } } } @@ -771,12 +843,13 @@ private void showKeyboard() { bottomDialogLayout.setTranslationY(bottomDialogLayout.getMeasuredHeight()); bottomDialogLayout.animate().setListener(null).cancel(); bottomDialogLayout.animate().translationY(0).withLayer().setDuration(350).setInterpolator(CubicBezierInterpolator.DEFAULT).setUpdateListener(animation -> { - actionButton.setTranslationY(-(float) animation.getAnimatedValue() * bottomDialogLayout.getMeasuredHeight()); + actionButtonContainer.setTranslationY(-(float) animation.getAnimatedValue() * bottomDialogLayout.getMeasuredHeight()); }).setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.startAllHeavyOperations, 512); - scrollView.fullScroll(ScrollView.FOCUS_DOWN); +// scrollView.fullScroll(ScrollView.FOCUS_DOWN); +// scrollView.smoothScrollTo(); } }).start(); } @@ -795,7 +868,7 @@ private boolean closeKeyboard() { NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.stopAllHeavyOperations, 512); bottomDialogLayout.animate().setListener(null).cancel(); bottomDialogLayout.animate().translationY(bottomDialogLayout.getMeasuredHeight()).setDuration(350).withLayer().setInterpolator(CubicBezierInterpolator.DEFAULT).setUpdateListener(animation -> { - actionButton.setTranslationY(-(1f - (float) animation.getAnimatedValue()) * bottomDialogLayout.getMeasuredHeight()); + actionButtonContainer.setTranslationY(-(1f - (float) animation.getAnimatedValue()) * bottomDialogLayout.getMeasuredHeight()); }).setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { 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 ced770f603a..9cc1686adb4 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 @@ -113,6 +113,7 @@ public static void initPaints(Theme.ResourcesProvider resourcesProvider) { Theme.ResourcesProvider resourcesProvider; private Integer scrimViewReaction; private float scrimProgress; + private boolean scrimDirection; int availableWidth; private int lastDrawnWidth; @@ -185,6 +186,7 @@ public void setMessage(MessageObject messageObject, boolean isSmall, boolean isT } } ReactionButton button = new ReactionLayoutButton(old, reactionCount, isSmall, isTag); + button.inGroup = messageObject.hasValidGroupId(); reactionButtons.add(button); hasPaidReaction = hasPaidReaction || button.paid; if (!isSmall && !isTag && messageObject.messageOwner.reactions.recent_reactions != null) { @@ -239,6 +241,7 @@ public void setMessage(MessageObject messageObject, boolean isSmall, boolean isT } if (isSmall && reactionCount.count > 1 && reactionCount.chosen) { ReactionButton button2 = new ReactionLayoutButton(null, reactionCount, isSmall, isTag); + button2.inGroup = messageObject.hasValidGroupId(); reactionButtons.add(button2); reactionButtons.get(0).isSelected = false; reactionButtons.get(1).isSelected = true; @@ -296,7 +299,7 @@ public void measure(int availableWidth, int gravity) { button.width += button.counterDrawable.getCurrentWidth() + dp(8); } } else { - button.width = (int) (dp(8) + dp(20) + dp(4)); + button.width = (int) (dp(8) + dp(20) + dp(button.animatedEmojiDrawable != null ? 6 : 4)); if (button.avatarsDrawable != null && button.users.size() > 0) { button.users.size(); int c1 = 1; @@ -389,7 +392,7 @@ public void draw(Canvas canvas, float animationProgress, Integer drawOnlyReactio alpha = animationProgress; canvas.scale(s, s, totalX + x + reactionButton.width / 2f, totalY + y + reactionButton.height / 2f); } - reactionButton.draw(canvas, totalX + x, totalY + y, reactionButton.animationType == ANIMATION_TYPE_MOVE ? animationProgress : 1f, alpha, drawOnlyReaction != null); + reactionButton.draw(canvas, totalX + x, totalY + y, reactionButton.animationType == ANIMATION_TYPE_MOVE ? animationProgress : 1f, alpha, drawOnlyReaction != null, scrimDirection, scrimProgress); canvas.restore(); } @@ -398,7 +401,7 @@ public void draw(Canvas canvas, float animationProgress, Integer drawOnlyReactio float s = 0.5f + 0.5f * (1f - animationProgress); canvas.save(); canvas.scale(s, s, totalX + reactionButton.x + reactionButton.width / 2f, totalY + reactionButton.y + reactionButton.height / 2f); - outButtons.get(i).draw(canvas, totalX + reactionButton.x, totalY + reactionButton.y, 1f, (1f - animationProgress), false); + outButtons.get(i).draw(canvas, totalX + reactionButton.x, totalY + reactionButton.y, 1f, (1f - animationProgress), false, scrimDirection, scrimProgress); canvas.restore(); } } @@ -637,6 +640,11 @@ public void setScrimProgress(float scrimProgress) { this.scrimProgress = scrimProgress; } + public void setScrimProgress(float scrimProgress, boolean direction) { + this.scrimProgress = scrimProgress; + this.scrimDirection = direction; + } + public class ReactionLayoutButton extends ReactionButton { public ReactionLayoutButton(ReactionButton reuseFrom, TLRPC.ReactionCount reactionCount, boolean isSmall, boolean isTag) { super(reuseFrom, currentAccount, parentView, reactionCount, isSmall, isTag, resourcesProvider); @@ -718,6 +726,8 @@ public static class ReactionButton { int animatedEmojiDrawableColor; public CounterView.CounterDrawable counterDrawable; public AnimatedTextView.AnimatedTextDrawable textDrawable; + public AnimatedTextView.AnimatedTextDrawable scrimPreviewCounterDrawable; + private boolean lastScrimProgressDirection; int backgroundColor; int textColor; int serviceBackgroundColor; @@ -728,6 +738,7 @@ public static class ReactionButton { public int lastDrawnTagDotColor; boolean isSelected; + public boolean inGroup; public boolean isTag; AvatarsDrawable avatarsDrawable; ArrayList users; @@ -765,10 +776,18 @@ public ReactionButton(ReactionButton reuseFrom, int currentAccount, View parentV textDrawable = new AnimatedTextView.AnimatedTextDrawable(true, true, true); textDrawable.setAnimationProperties(.4f, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); textDrawable.setTextSize(dp(13)); - textDrawable.setCallback(parentView); + textDrawable.setCallback(supercallback); textDrawable.setTypeface(AndroidUtilities.bold()); textDrawable.setOverrideFullWidth(AndroidUtilities.displaySize.x); } + if (scrimPreviewCounterDrawable == null) { + scrimPreviewCounterDrawable = new AnimatedTextView.AnimatedTextDrawable(false, false, false, true); + scrimPreviewCounterDrawable.setTextSize(dp(12)); + scrimPreviewCounterDrawable.setCallback(supercallback); + scrimPreviewCounterDrawable.setTypeface(AndroidUtilities.bold()); + scrimPreviewCounterDrawable.setOverrideFullWidth(AndroidUtilities.displaySize.x); + scrimPreviewCounterDrawable.setScaleProperty(.35f); + } this.reactionCount = reactionCount; this.reaction = reactionCount.reaction; this.visibleReaction = VisibleReaction.fromTL(reactionCount.reaction); @@ -895,7 +914,7 @@ public void drawOverlay(Canvas canvas, float x, float y, float progress, float a canvas.restore(); } - public void draw(Canvas canvas, float x, float y, float progress, float alpha, boolean drawOverlayScrim) { + public void draw(Canvas canvas, float x, float y, float progress, float alpha, boolean drawOverlayScrim, boolean scrimProgressDirection, float scrimProgress) { wasDrawn = true; ImageReceiver imageReceiver = animatedEmojiDrawable != null ? animatedEmojiDrawable.getImageReceiver() : this.imageReceiver; if (isSmall && imageReceiver != null) { @@ -949,10 +968,25 @@ public void draw(Canvas canvas, float x, float y, float progress, float alpha, b imageReceiver.setAlpha(alpha); } + if (scrimProgress > 0 && lastScrimProgressDirection != scrimProgressDirection) { + if (scrimProgressDirection) { + scrimPreviewCounterDrawable.setAnimationProperties(.6f, 0, 650, 1.6f, CubicBezierInterpolator.EASE_OUT_BACK); + scrimPreviewCounterDrawable.setText(AndroidUtilities.formatWholeNumber(count, 0), false); + scrimPreviewCounterDrawable.setText(LocaleController.formatNumber(count, ','), true); + } else { + scrimPreviewCounterDrawable.setAnimationProperties(.6f, 0, 320, 1.6f, CubicBezierInterpolator.EASE_OUT_QUINT); + scrimPreviewCounterDrawable.setText(AndroidUtilities.formatWholeNumber(count, 0), true); + } + lastScrimProgressDirection = scrimProgressDirection; + } + final float bounceScale = bounce.getScale(0.1f); boolean restore = false; int w = width; - if (progress != 1f && animationType == ANIMATION_TYPE_MOVE) { + if (scrimProgress > 0 && !isTag && scrimPreviewCounterDrawable != null && avatarsDrawable == null) { + w = (int) (dp(8) + dp(20) + dp(animatedEmojiDrawable != null ? 6 : 4) + scrimPreviewCounterDrawable.getCurrentWidth() + dp(8)); + scrimPreviewCounterDrawable.setTextColor(lastDrawnTextColor); + } else if (progress != 1f && animationType == ANIMATION_TYPE_MOVE) { w = (int) (width * progress + animateFromWidth * (1f - progress)); } AndroidUtilities.rectTmp.set(x, y, x + w, y + height); @@ -1036,9 +1070,16 @@ public void draw(Canvas canvas, float x, float y, float progress, float alpha, b canvas.restore(); tx = textDrawable.getCurrentWidth() + dp(4) * textDrawable.isNotEmpty(); } - if (counterDrawable != null && drawCounter()) { + if (scrimProgress > 0.0f && !isTag && scrimPreviewCounterDrawable != null && avatarsDrawable == null) { canvas.save(); - canvas.translate(x + dp(hasName && !drawTagDot() ? 10 : (hasName ? 9 : 8)) + dp(20) + dp(2) + tx + (paid ? -dp(1) : 0), y); + canvas.translate(x + dp(hasName && !drawTagDot() ? 10 : (hasName ? 9 : 8)) + dp(20) + dp(animatedEmojiDrawable != null ? 5 : 2), y - dp(1)); + scrimPreviewCounterDrawable.setBounds(0, 0, width, height); + scrimPreviewCounterDrawable.draw(canvas); + scrimPreviewCounterDrawable.setAlpha((int) (0xFF * alpha)); + canvas.restore(); + } else if (counterDrawable != null && drawCounter()) { + canvas.save(); + canvas.translate(x + dp(hasName && !drawTagDot() ? 10 : (hasName ? 9 : 8)) + dp(20) + dp(animatedEmojiDrawable != null ? 5 : 2) + tx + (paid ? -dp(1) : 0), y); counterDrawable.draw(canvas); canvas.restore(); } @@ -1294,6 +1335,58 @@ public void detachPreview() { previewAnimatedEmojiDrawable = null; } } + + private final Drawable.Callback callback = new Drawable.Callback() { + @Override + public void invalidateDrawable(@NonNull Drawable who) { + if (parentView != null) { + parentView.invalidate(); + if (inGroup && parentView.getParent() instanceof View) { + ((View) parentView.getParent()).invalidate(); + } + } + } + + @Override + public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) { + if (parentView != null) { + parentView.scheduleDrawable(who, what, when); + } + } + + @Override + public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) { + if (parentView != null) { + parentView.unscheduleDrawable(who, what); + } + } + }; + + private final Drawable.Callback supercallback = new Drawable.Callback() { + @Override + public void invalidateDrawable(@NonNull Drawable who) { + if (parentView != null) { + parentView.invalidate(); + if (inGroup && parentView.getParent() != null && parentView.getParent().getParent() instanceof View) { + ((View) parentView.getParent().getParent()).invalidate(); + } + } + } + + @Override + public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) { + if (parentView != null) { + parentView.scheduleDrawable(who, what, when); + } + } + + @Override + public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) { + if (parentView != null) { + parentView.unscheduleDrawable(who, what); + } + } + }; } float lastX; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchTagsList.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchTagsList.java index ebdd612dcdf..e851f23fd1f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchTagsList.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchTagsList.java @@ -901,7 +901,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @Override protected void onDraw(Canvas canvas) { - reactionButton.draw(canvas, (getWidth() - reactionButton.width) / 2f, (getHeight() - reactionButton.height) / 2f, progress.set(1f), 1f, false); + reactionButton.draw(canvas, (getWidth() - reactionButton.width) / 2f, (getHeight() - reactionButton.height) / 2f, progress.set(1f), 1f, false, false, 0.0f); } private boolean attached; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchViewPager.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchViewPager.java index c42de0af09a..f7bf4c26722 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchViewPager.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchViewPager.java @@ -90,6 +90,14 @@ public class SearchViewPager extends ViewPagerFixed implements FilteredSearchVie public RecyclerListView botsSearchListView; public DialogsBotsAdapter botsSearchAdapter; + public boolean expandedPublicPosts = false; + private DefaultItemAnimator hashtagItemAnimator; + public FrameLayout hashtagSearchContainer; + public StickerEmptyView hashtagEmptyView; + private LinearLayoutManager hashtagSearchLayoutManager; + public RecyclerListView hashtagSearchListView; + public HashtagsSearchAdapter hashtagSearchAdapter; + private NumberTextView selectedMessagesCountTextView; private boolean isActionModeShowed; private HashMap selectedFiles = new HashMap<>(); @@ -160,6 +168,28 @@ public void notifyDataSetChanged() { emptyView.showProgress(false, false); } } + + @Override + protected void openPublicPosts() { + hashtagSearchAdapter.setInitialData(dialogsSearchAdapter.publicPostsHashtag, dialogsSearchAdapter.publicPosts, dialogsSearchAdapter.publicPostsLastRate, dialogsSearchAdapter.publicPostsTotalCount); + expandedPublicPosts = true; + hashtagSearchLayoutManager.scrollToPositionWithOffset(0, 0); + updateTabs(); + if (tabsView != null && tabsView.getCurrentTabId() != 1) { + tabsView.scrollToTab(1, 1); + } + hashtagSearchAdapter.search(lastSearchString); + } + + @Override + protected void openBotApp(TLRPC.User bot) { + if (bot == null) return; + if (parent instanceof DialogsActivity) { + ((DialogsActivity) parent).closeSearching(); + } + MessagesController.getInstance(currentAccount).openApp(bot, 0); + putRecentSearch(bot.id, bot); + } }; if (initialDialogsType == DialogsActivity.DIALOGS_TYPE_BOT_REQUEST_PEER) { ArrayList dialogs = fragment.getDialogsArray(currentAccount, initialDialogsType, folderId, true); @@ -413,6 +443,77 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { } }); + hashtagSearchContainer = new FrameLayout(context); + + hashtagItemAnimator = new DefaultItemAnimator() { + @Override + protected void onMoveAnimationUpdate(RecyclerView.ViewHolder holder) { + super.onMoveAnimationUpdate(holder); + invalidate(); + } + }; + hashtagItemAnimator.setSupportsChangeAnimations(false); + hashtagItemAnimator.setDelayAnimations(false); + hashtagItemAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + hashtagItemAnimator.setDurations(350); + + hashtagSearchListView = new BlurredRecyclerView(context); + hashtagSearchListView.setItemAnimator(hashtagItemAnimator); + hashtagSearchListView.setPivotY(0); + hashtagSearchListView.setVerticalScrollBarEnabled(true); + hashtagSearchListView.setInstantClick(true); + hashtagSearchListView.setVerticalScrollbarPosition(LocaleController.isRTL ? RecyclerListView.SCROLLBAR_POSITION_LEFT : RecyclerListView.SCROLLBAR_POSITION_RIGHT); + hashtagSearchListView.setLayoutManager(hashtagSearchLayoutManager = new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); + hashtagSearchListView.setAnimateEmptyView(true, RecyclerListView.EMPTY_VIEW_ANIMATION_TYPE_ALPHA); + + loadingView = new FlickerLoadingView(context); + loadingView.setViewType(1); + hashtagEmptyView = new StickerEmptyView(context, loadingView, StickerEmptyView.STICKER_TYPE_SEARCH) { + @Override + public void setVisibility(int visibility) { + if (noMediaFiltersSearchView.getTag() != null) { + super.setVisibility(View.GONE); + return; + } + super.setVisibility(visibility); + } + }; + hashtagEmptyView.title.setText(LocaleController.getString(R.string.NoResult)); + hashtagEmptyView.subtitle.setVisibility(View.GONE); + hashtagEmptyView.setVisibility(View.GONE); + hashtagEmptyView.addView(loadingView, 0); + hashtagEmptyView.showProgress(true, false); + hashtagSearchContainer.addView(hashtagEmptyView); + hashtagSearchContainer.addView(hashtagSearchListView); + hashtagSearchListView.setEmptyView(hashtagEmptyView); + hashtagSearchListView.setAdapter(hashtagSearchAdapter = new HashtagsSearchAdapter(hashtagSearchListView, context, currentAccount, folderId, null) { + @Override + public void update(boolean animated) { + super.update(animated); + hashtagEmptyView.showProgress(false, animated); + hashtagEmptyView.title.setText(LocaleController.getString(R.string.NoResult)); + hashtagEmptyView.subtitle.setVisibility(View.GONE); + } + + @Override + protected void scrollToTop(boolean ifAtTop) { + if (ifAtTop && hashtagSearchListView.canScrollVertically(-1)) return; + hashtagSearchLayoutManager.scrollToPositionWithOffset(0, 0); + } + }); + hashtagSearchListView.setOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + if (newState == RecyclerView.SCROLL_STATE_DRAGGING) { + AndroidUtilities.hideKeyboard(fragment.getParentActivity().getCurrentFocus()); + } + } + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + hashtagSearchAdapter.checkBottom(); + } + }); + itemsEnterAnimator = new RecyclerItemsEnterAnimator(searchListView, true); setAdapter(viewPagerAdapter = new ViewPagerAdapter()); @@ -444,13 +545,27 @@ protected long getDialogId(String query) { } public void updateTabs() { + updateTabs(false); + } + + public void updateTabs(boolean animated) { viewPagerAdapter.updateItems(); - fillTabs(false); + fillTabs(animated); if (tabsView != null) { tabsView.finishAddingTabs(); } } + public boolean includeFolder() { + for (int i = 0; i < currentSearchFilters.size(); i++) { + FiltersView.MediaFilterData data = currentSearchFilters.get(i); + if (data.filterType == FiltersView.FILTER_TYPE_ARCHIVE) { + return true; + } + } + return false; + } + private void search(View view, int position, String query, boolean reset) { if (TextUtils.isEmpty(query)) { emptyView.subtitle.setVisibility(View.GONE); @@ -479,21 +594,33 @@ private void search(View view, int position, String query, boolean reset) { } } + if (hashtagSearchAdapter.getHashtag(query) == null) { + collapsePublicPosts(); + } + if (view == channelsSearchContainer) { MessagesController.getInstance(currentAccount).getChannelRecommendations(0); channelsSearchAdapter.search(query); channelsEmptyView.setKeyboardHeight(keyboardSize, false); } else if (view == botsSearchContainer) { -// MessagesController.getInstance(currentAccount).getChannelRecommendations(0); botsSearchAdapter.search(query); botsEmptyView.setKeyboardHeight(keyboardSize, false); if (TextUtils.isEmpty(query)) { botsSearchAdapter.checkBottom(); } + } else if (view == hashtagSearchContainer) { + if (hashtagSearchAdapter.getHashtag(query) == null) { + return; + } + if (reset) { + hashtagSearchLayoutManager.scrollToPositionWithOffset(0, 0); + } + hashtagSearchAdapter.search(query); + hashtagEmptyView.setKeyboardHeight(keyboardSize, false); } else if (view == searchContainer) { if (dialogId == 0 && minDate == 0 && maxDate == 0 || forumDialogId != 0) { lastSearchScrolledToTop = false; - dialogsSearchAdapter.searchDialogs(query, includeFolder ? 1 : 0); + dialogsSearchAdapter.searchDialogs(query, includeFolder ? 1 : 0, true); dialogsSearchAdapter.setFiltersDelegate(filteredSearchViewDelegate, false); noMediaFiltersSearchView.animate().setListener(null).cancel(); noMediaFiltersSearchView.setDelegate(null, false); @@ -570,6 +697,19 @@ public ArrayList getCurrentSearchFilters() { public void clear() { currentSearchFilters.clear(); + collapsePublicPosts(); + } + + public void collapsePublicPosts() { + if (!expandedPublicPosts) return; + expandedPublicPosts = false; + updateTabs(); + if (tabsView != null && tabsView.getCurrentTabId() != 0) { + tabsView.scrollToTab(0, 0); + } + if (dialogsSearchAdapter != null) { + dialogsSearchAdapter.searchDialogs(lastSearchString, includeFolder() ? 1 : 0, true); + } } public void setFilteredSearchViewDelegate(FilteredSearchView.Delegate filteredSearchViewDelegate) { @@ -963,6 +1103,9 @@ public void reset() { if (botsSearchLayoutManager != null) { botsSearchLayoutManager.scrollToPositionWithOffset(0, 0); } + if (hashtagSearchLayoutManager != null) { + hashtagSearchLayoutManager.scrollToPositionWithOffset(0, 0); + } viewsByType.clear(); } @@ -1062,6 +1205,7 @@ protected void onAttachedToWindow() { NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.dialogDeleted); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.dialogsNeedReload); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.reloadWebappsHints); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.storiesListUpdated); attached = true; if (channelsSearchAdapter != null) { @@ -1080,6 +1224,7 @@ protected void onDetachedFromWindow() { NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.dialogDeleted); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.dialogsNeedReload); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.reloadWebappsHints); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.storiesListUpdated); } @Override @@ -1093,6 +1238,10 @@ public void didReceivedNotification(int id, int account, Object... args) { channelsSearchAdapter.update(true); } else if (id == NotificationCenter.reloadWebappsHints) { botsSearchAdapter.update(true); + } else if (id == NotificationCenter.storiesListUpdated) { + if (args[0] == hashtagSearchAdapter.list) { + hashtagSearchAdapter.update(true); + } } } @@ -1108,7 +1257,7 @@ public void cancelEnterAnimation() { } public void showDownloads() { - setPosition(4); + setPosition((expandedPublicPosts ? 1 : 0) + 4); } public int getPositionForType(int initialSearchType) { @@ -1129,6 +1278,7 @@ private class ViewPagerAdapter extends ViewPagerFixed.Adapter { private final static int DOWNLOADS_TYPE = 2; private final static int FILTER_TYPE = 3; private final static int BOTS_TYPE = 4; + private final static int PUBLIC_POSTS_TYPE = 5; public ViewPagerAdapter() { updateItems(); @@ -1137,6 +1287,9 @@ public ViewPagerAdapter() { public void updateItems() { items.clear(); items.add(new Item(DIALOGS_TYPE)); + if (expandedPublicPosts) { + items.add(new Item(PUBLIC_POSTS_TYPE)); + } items.add(new Item(CHANNELS_TYPE)); items.add(new Item(BOTS_TYPE)); if (!showOnlyDialogsAdapter) { @@ -1171,6 +1324,8 @@ public String getItemTitle(int position) { return LocaleController.getString(R.string.AppsTab); } else if (items.get(position).type == DOWNLOADS_TYPE) { return LocaleController.getString(R.string.DownloadsTabs); + } else if (items.get(position).type == PUBLIC_POSTS_TYPE) { + return LocaleController.getString(R.string.PublicPostsTabs); } else { return FiltersView.filters[items.get(position).filterIndex].getTitle(); } @@ -1189,6 +1344,8 @@ public View createView(int viewType) { return channelsSearchContainer; } else if (viewType == 4) { return botsSearchContainer; + } else if (viewType == 5) { + return hashtagSearchContainer; } else if (viewType == 2) { downloadsContainer = new SearchDownloadsContainer(parent, currentAccount); downloadsContainer.recyclerListView.addOnScrollListener(new RecyclerView.OnScrollListener() { @@ -1229,6 +1386,9 @@ public int getItemViewType(int position) { if (items.get(position).type == DOWNLOADS_TYPE) { return 2; } + if (items.get(position).type == PUBLIC_POSTS_TYPE) { + return 5; + } return items.get(position).type + position; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SeekSpeedDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SeekSpeedDrawable.java new file mode 100644 index 00000000000..2f1ebf9d966 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SeekSpeedDrawable.java @@ -0,0 +1,276 @@ +package org.telegram.ui.Components; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.CornerPathEffect; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.view.Gravity; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.messenger.SharedConfig; +import org.telegram.ui.ActionBar.Theme; + +import java.util.Locale; + +public class SeekSpeedDrawable extends Drawable { + + private final boolean isRound, isPiP; + + private Runnable invalidate; + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint arrowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private RLottieDrawable hintDrawable; + private final Path hintArrow = new Path(); + private final Text hintText = new Text(LocaleController.getString(R.string.SeekSpeedHint), 14); + + private final Path leftArrow = new Path(); + private final Path rightArrow = new Path(); + + private boolean shown; + private int direction = +1; + private final AnimatedFloat animatedShown; + private final AnimatedFloat animatedDirection; + private final AnimatedFloat animatedSpeed; + private final AnimatedFloat animatedHintShown; + + private final AnimatedTextView.AnimatedTextDrawable speedText; + + private boolean showHint; + + public SeekSpeedDrawable(Runnable invalidate, boolean isPiP, boolean isRound) { + this.invalidate = invalidate; + this.isPiP = isPiP; + this.isRound = isRound; + + animatedShown = new AnimatedFloat(invalidate, 0, 360, CubicBezierInterpolator.EASE_OUT_QUINT); + animatedShown.set(false, true); + animatedDirection = new AnimatedFloat(invalidate, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + animatedSpeed = new AnimatedFloat(invalidate, 0, 200, CubicBezierInterpolator.EASE_OUT_QUINT); + animatedHintShown = new AnimatedFloat(invalidate, 0, 360, CubicBezierInterpolator.EASE_OUT_QUINT); + animatedHintShown.set(false, true); + + speedText = new AnimatedTextView.AnimatedTextDrawable(false, true, true, true) { + @Override + public void invalidateSelf() { + invalidate.run(); + } + }; + speedText.setScaleProperty(.3f); + speedText.setAnimationProperties(.4f, 0, 650, 1.6f, CubicBezierInterpolator.EASE_OUT_QUINT); + speedText.setTypeface(AndroidUtilities.getTypeface("fonts/num.otf")); + speedText.setTextSize(dp(16)); + setSpeed(2.0f, false); + speedText.setTextColor(0xFFFFFFFF); + speedText.setGravity(Gravity.CENTER); + + arrowPaint.setPathEffect(new CornerPathEffect(dp(1.66f))); + + leftArrow.moveTo(dp(8.66f), -dp(12.66f / 2.0f)); + leftArrow.lineTo(0, 0); + leftArrow.lineTo(dp(8.66f), dp(12.66f / 2.0f)); + leftArrow.close(); + + rightArrow.moveTo(0, -dp(12.66f / 2.0f)); + rightArrow.lineTo(dp(8.66f), 0); + rightArrow.lineTo(0, dp(12.66f / 2.0f)); + rightArrow.close(); + + showHint = !isPiP && !isRound && !MessagesController.getGlobalMainSettings().getBoolean("seekSpeedHintShowed", false); + + hintArrow.moveTo(-dp(6.5f), 0); + hintArrow.lineTo(0, -dp(6.33f)); + hintArrow.lineTo(dp(6.5f), 0); + hintArrow.close(); + } + + public boolean isShown() { + return shown || animatedShown.get() > 0; + } + + private final RectF speedRect = new RectF(); + private final RectF hintRect = new RectF(); + + private float t; + private long lastFrameTime; + + @Override + public void draw(@NonNull Canvas canvas) { + final Rect bounds = getBounds(); + final float speedRectWidth = speedText.getCurrentWidth() + dp(9 + 8 + 20 + 9); + + final float shown = animatedShown.set(this.shown); + final float direction = animatedDirection.set(this.direction); + if (shown <= 0.0f) return; + final float speed = animatedSpeed.set(Math.abs(this.lastSpeed)); + + final long now = System.currentTimeMillis(); + final float deltaTime = Math.min(.016f, (now - lastFrameTime) / 1000.0f); + lastFrameTime = now; + t += deltaTime * (1.5f * Math.min(speed, 4.0f)); + invalidate.run(); + + speedRect.set(bounds.centerX() - speedRectWidth / 2f, bounds.top + dp(9), bounds.centerX() + speedRectWidth / 2f, bounds.top + dp(9 + 28)); + canvas.save(); + float scale = .6f + .4f * shown; + if (bounds.width() < AndroidUtilities.displaySize.x * .7f) { + scale *= .75f; + if (isPiP) { + canvas.translate(-dp(45), 0); + } + } + canvas.scale(scale, scale, speedRect.centerX(), speedRect.top); + canvas.translate(0, -dp(15) * (1.0f - shown)); + canvas.clipRect(speedRect); + + backgroundPaint.setColor(Theme.multAlpha(0xFF000000, 0.4f * shown)); + canvas.drawRoundRect(speedRect, speedRect.height() / 2f, speedRect.height() / 2f, backgroundPaint); + speedText.setBounds(speedRect); + + float p; + canvas.save(); + canvas.translate(speedRect.centerX() - speedRectWidth / 2.0f + dp(9) - dp(30) * (1.0f - Math.max(0, -direction)), speedRect.centerY()); + p = ((float)Math.sin((t) * Math.PI)/2.0f+1.0f); + arrowPaint.setColor(Theme.multAlpha(0xFFFFFFFF, Math.max(0, -direction) * shown * (.2f + .75f * p))); + canvas.drawPath(leftArrow, arrowPaint); + canvas.translate(dp(10.66f), 0); + p = ((float)Math.sin((t+.17f) * Math.PI)/2.0f+1.0f); + arrowPaint.setColor(Theme.multAlpha(0xFFFFFFFF, Math.max(0, -direction) * shown * (.2f + .75f * p))); + canvas.drawPath(leftArrow, arrowPaint); + canvas.restore(); + + canvas.save(); + canvas.translate(-dp(20 + 8) / 2.0f * direction, 0.0f); + speedText.setAlpha((int) (0xFF * shown)); + speedText.draw(canvas); + canvas.restore(); + + canvas.save(); + canvas.translate(speedRect.centerX() + speedRectWidth / 2.0f - dp(30) + dp(30) * (1.0f - Math.max(0, direction)), speedRect.centerY()); + p = ((float)Math.sin((t) * Math.PI)/2.0f+1.0f); + arrowPaint.setColor(Theme.multAlpha(0xFFFFFFFF, Math.max(0, direction) * shown * (.2f + .75f * p))); + canvas.drawPath(rightArrow, arrowPaint); + canvas.translate(dp(10.66f), 0); + p = ((float)Math.sin((t-.17f) * Math.PI)/2.0f+1.0f); + arrowPaint.setColor(Theme.multAlpha(0xFFFFFFFF, Math.max(0, direction) * shown * (.2f + .75f * p))); + canvas.drawPath(rightArrow, arrowPaint); + canvas.restore(); + + canvas.restore(); + + final float hintShown = animatedHintShown.set(this.showHint && this.shown); + if (hintShown > 0) { + if (hintDrawable == null) { + hintDrawable = new RLottieDrawable(R.raw.seek_speed_hint, "" + R.raw.seek_speed_hint, AndroidUtilities.dp(24), AndroidUtilities.dp(24), true, null); + hintDrawable.setAllowDecodeSingleFrame(true); + hintDrawable.setCallback(new Callback() { + @Override + public void invalidateDrawable(@NonNull Drawable who) { + invalidate.run(); + } + @Override + public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {} + @Override + public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {} + }); + hintDrawable.setAutoRepeat(1); + hintDrawable.start(); + } + final float hintW = hintText.getCurrentWidth() + dp(22 + 24 + 8); + final float hintH = dp(32); + hintRect.set(bounds.centerX() - hintW / 2.0f, speedRect.top + speedRect.height() * shown + dp(11), bounds.centerX() + hintW / 2.0f, speedRect.top + speedRect.height() * shown + dp(11) + hintH); + + canvas.save(); + final float hintScale = .75f + .25f * hintShown; + canvas.scale(hintScale, hintScale, hintRect.centerX(), hintRect.top); + backgroundPaint.setColor(Theme.multAlpha(0xFF000000, 0.4f * hintShown)); + + canvas.save(); + canvas.translate(hintRect.centerX(), hintRect.top); + canvas.drawPath(hintArrow, backgroundPaint); + canvas.restore(); + canvas.drawRoundRect(hintRect, dp(8), dp(8), backgroundPaint); + + hintDrawable.setBounds((int) hintRect.left + dp(11), (int) hintRect.centerY() - dp(24) / 2, (int) hintRect.left + dp(11 + 24), (int) hintRect.centerY() + dp(24) / 2); + hintDrawable.setAlpha((int) (0xFF * hintShown)); + if (!hintDrawable.isRunning()) { + hintDrawable.restart(true); + } + hintDrawable.draw(canvas); + + hintText.draw(canvas, hintRect.left + dp(11 + 24 + 4), hintRect.centerY(), 0xFFFFFFFF, hintShown); + + canvas.restore(); + } + } + + public void setShown(boolean shown, boolean animated) { + this.shown = shown; + if (!animated) { + animatedShown.set(shown, true); + } + invalidate.run(); + + if (hintDrawable != null && showHint) { + if (shown) { + hintDrawable.restart(); + } else { + hintDrawable.stop(); + } + } + } + + private float lastSpeed; + public void setSpeed(float speed, boolean animated) { + if (Math.floor(lastSpeed * 10) != Math.floor(speed * 10)) { + speedText.cancelAnimation(); + speedText.setText(String.format(Locale.US, "%.1fx", Math.abs(speed)), animated); + lastSpeed = speed; + } + direction = speed > 0 ? +1 : -1; + if (!animated) { + animatedDirection.set(direction, true); + } + invalidate.run(); + + if (showHint && Math.abs(speed) > 3.0f && !hideHintScheduled) { + hideHintScheduled = true; + AndroidUtilities.runOnUIThread(hideHintRunnable, 2500); + MessagesController.getGlobalMainSettings().edit().putBoolean("seekSpeedHintShowed", true).apply(); + } + } + + private boolean hideHintScheduled; + private final Runnable hideHintRunnable = () -> { + showHint = false; + this.invalidate.run(); + }; + + @Override + public void setAlpha(int alpha) { + + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSPARENT; + } +} 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 20be47b0378..57526536615 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java @@ -628,7 +628,9 @@ public void updateFastScrollVisibility(MediaPage mediaPage, boolean animated) { private GroupUsersSearchAdapter groupUsersSearchAdapter; private MediaPage[] mediaPages = new MediaPage[2]; private ActionBarMenuItem deleteItem; + @Nullable public ActionBarMenuItem searchItemIcon; + @Nullable private ActionBarMenuItem searchItem; private float searchAlpha; private float optionsAlpha; @@ -789,6 +791,7 @@ public SharedMediaPreloader(BaseFragment fragment) { } loadMediaCounts(); + if (parentFragment == null) return; NotificationCenter notificationCenter = parentFragment.getNotificationCenter(); notificationCenter.addObserver(this, NotificationCenter.mediaCountsDidLoad); notificationCenter.addObserver(this, NotificationCenter.mediaCountDidLoad); @@ -816,6 +819,7 @@ public void onDestroy(BaseFragment fragment) { return; } delegates.clear(); + if (parentFragment == null) return; NotificationCenter notificationCenter = parentFragment.getNotificationCenter(); notificationCenter.removeObserver(this, NotificationCenter.mediaCountsDidLoad); notificationCenter.removeObserver(this, NotificationCenter.mediaCountDidLoad); @@ -1109,6 +1113,7 @@ public void run() { } private void loadMediaCounts() { + if (parentFragment == null) return; parentFragment.getMediaDataController().getMediaCounts(dialogId, topicId, parentFragment.getClassGuid()); if (mergeDialogId != 0) { parentFragment.getMediaDataController().getMediaCounts(mergeDialogId, topicId, parentFragment.getClassGuid()); @@ -1116,6 +1121,7 @@ private void loadMediaCounts() { } private void setChatInfo(TLRPC.ChatFull chatInfo) { + if (parentFragment == null) return; if (chatInfo != null && chatInfo.migrated_from_chat_id != 0 && mergeDialogId == 0) { mergeDialogId = -chatInfo.migrated_from_chat_id; parentFragment.getMediaDataController().getMediaCounts(mergeDialogId, topicId, parentFragment.getClassGuid()); @@ -1570,135 +1576,137 @@ public boolean needPlayMessage(MessageObject messageObject) { savedDialogsAdapter.unselectAll(); } - final ActionBarMenu menu = actionBar.createMenu(); - menu.addOnLayoutChangeListener(new OnLayoutChangeListener() { - @Override - public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7) { - if (searchItem == null) { - return; - } - View parent = (View) searchItem.getParent(); - searchItem.setTranslationX(parent.getMeasuredWidth() - searchItem.getRight()); - } - }); - if (dialog_id == profileActivity.getUserConfig().getClientUserId() && profileActivity instanceof MediaActivity && canShowSearchItem()) { - searchItemIcon = menu.addItem(11, R.drawable.ic_ab_search); - } - searchItem = menu.addItem(0, 0).setIsSearchField(true).setActionBarMenuItemSearchListener(new ActionBarMenuItem.ActionBarMenuItemSearchListener() { - @Override - public void onSearchExpand() { - searching = true; - if (searchTagsList != null) { - searchTagsList.show((getSelectedTab() == TAB_SAVED_DIALOGS || getSelectedTab() == TAB_SAVED_MESSAGES) && searchTagsList.hasFilters()); - } - if (photoVideoOptionsItem != null) { - photoVideoOptionsItem.setVisibility(View.GONE); - } - if (searchItemIcon != null) { - searchItemIcon.setVisibility(View.GONE); - } - searchItem.setVisibility(View.GONE); - onSearchStateChanged(true); - if (optionsSearchImageView != null) { - optionsSearchImageView.animate().scaleX(0.6f).scaleY(0.6f).alpha(0).setDuration(320).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); + if (addActionButtons()) { + final ActionBarMenu menu = actionBar.createMenu(); + menu.addOnLayoutChangeListener(new OnLayoutChangeListener() { + @Override + public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7) { + if (searchItem == null) { + return; + } + View parent = (View) searchItem.getParent(); + searchItem.setTranslationX(parent.getMeasuredWidth() - searchItem.getRight()); } + }); + if (dialog_id == profileActivity.getUserConfig().getClientUserId() && profileActivity instanceof MediaActivity && canShowSearchItem()) { + searchItemIcon = menu.addItem(11, R.drawable.ic_ab_search); } - - @Override - public void onSearchCollapse() { - searching = false; - searchingReaction = null; - if (searchItemIcon != null) { - searchItemIcon.setVisibility(View.VISIBLE); - } - if (photoVideoOptionsItem != null && getPhotoVideoOptionsAlpha(0) > .5f) { - photoVideoOptionsItem.setVisibility(View.VISIBLE); - } - if (searchTagsList != null) { - searchTagsList.clear(); - searchTagsList.show(false); - } - if (savedMessagesContainer != null) { - savedMessagesContainer.chatActivity.clearSearch(); - } - searchWas = false; - searchItem.setVisibility(View.VISIBLE); - documentsSearchAdapter.search(null, true); - linksSearchAdapter.search(null, true); - audioSearchAdapter.search(null, true); - groupUsersSearchAdapter.search(null, true); - if (savedMessagesSearchAdapter != null) { - savedMessagesSearchAdapter.search(null, null); - } - onSearchStateChanged(false); - if (optionsSearchImageView != null) { - optionsSearchImageView.animate().scaleX(1f).scaleY(1f).alpha(1f).setDuration(320).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); - } - if (ignoreSearchCollapse) { - ignoreSearchCollapse = false; - return; + searchItem = menu.addItem(0, 0).setIsSearchField(true).setActionBarMenuItemSearchListener(new ActionBarMenuItem.ActionBarMenuItemSearchListener() { + @Override + public void onSearchExpand() { + searching = true; + if (searchTagsList != null) { + searchTagsList.show((getSelectedTab() == TAB_SAVED_DIALOGS || getSelectedTab() == TAB_SAVED_MESSAGES) && searchTagsList.hasFilters()); + } + if (photoVideoOptionsItem != null) { + photoVideoOptionsItem.setVisibility(View.GONE); + } + if (searchItemIcon != null) { + searchItemIcon.setVisibility(View.GONE); + } + searchItem.setVisibility(View.GONE); + onSearchStateChanged(true); + if (optionsSearchImageView != null) { + optionsSearchImageView.animate().scaleX(0.6f).scaleY(0.6f).alpha(0).setDuration(320).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); + } } - switchToCurrentSelectedMode(false); - } - @Override - public void onTextChanged(EditText editText) { - String text = editText.getText().toString(); - if (savedMessagesContainer != null) { - savedMessagesContainer.chatActivity.setSearchQuery(text); - if (TextUtils.isEmpty(text) && searchingReaction == null) { - savedMessagesContainer.chatActivity.clearSearch(); + @Override + public void onSearchCollapse() { + searching = false; + searchingReaction = null; + if (searchItemIcon != null) { + searchItemIcon.setVisibility(View.VISIBLE); } - } - searchItem.setVisibility(View.GONE); - searchWas = text.length() != 0 || searchingReaction != null; - post(() -> switchToCurrentSelectedMode(false)); - if (mediaPages[0].selectedType == TAB_FILES) { - if (documentsSearchAdapter == null) { - return; + if (photoVideoOptionsItem != null && getPhotoVideoOptionsAlpha(0) > .5f) { + photoVideoOptionsItem.setVisibility(View.VISIBLE); } - documentsSearchAdapter.search(text, true); - } else if (mediaPages[0].selectedType == TAB_LINKS) { - if (linksSearchAdapter == null) { - return; + if (searchTagsList != null) { + searchTagsList.clear(); + searchTagsList.show(false); } - linksSearchAdapter.search(text, true); - } else if (mediaPages[0].selectedType == TAB_AUDIO) { - if (audioSearchAdapter == null) { - return; + if (savedMessagesContainer != null) { + savedMessagesContainer.chatActivity.clearSearch(); } - audioSearchAdapter.search(text, true); - } else if (mediaPages[0].selectedType == TAB_GROUPUSERS) { - if (groupUsersSearchAdapter == null) { + searchWas = false; + searchItem.setVisibility(View.VISIBLE); + documentsSearchAdapter.search(null, true); + linksSearchAdapter.search(null, true); + audioSearchAdapter.search(null, true); + groupUsersSearchAdapter.search(null, true); + if (savedMessagesSearchAdapter != null) { + savedMessagesSearchAdapter.search(null, null); + } + onSearchStateChanged(false); + if (optionsSearchImageView != null) { + optionsSearchImageView.animate().scaleX(1f).scaleY(1f).alpha(1f).setDuration(320).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); + } + if (ignoreSearchCollapse) { + ignoreSearchCollapse = false; return; } - groupUsersSearchAdapter.search(text, true); - } else if (mediaPages[0].selectedType == TAB_SAVED_DIALOGS) { - if (savedMessagesSearchAdapter == null) { - return; + switchToCurrentSelectedMode(false); + } + + @Override + public void onTextChanged(EditText editText) { + String text = editText.getText().toString(); + if (savedMessagesContainer != null) { + savedMessagesContainer.chatActivity.setSearchQuery(text); + if (TextUtils.isEmpty(text) && searchingReaction == null) { + savedMessagesContainer.chatActivity.clearSearch(); + } + } + searchItem.setVisibility(View.GONE); + searchWas = text.length() != 0 || searchingReaction != null; + post(() -> switchToCurrentSelectedMode(false)); + if (mediaPages[0].selectedType == TAB_FILES) { + if (documentsSearchAdapter == null) { + return; + } + documentsSearchAdapter.search(text, true); + } else if (mediaPages[0].selectedType == TAB_LINKS) { + if (linksSearchAdapter == null) { + return; + } + linksSearchAdapter.search(text, true); + } else if (mediaPages[0].selectedType == TAB_AUDIO) { + if (audioSearchAdapter == null) { + return; + } + audioSearchAdapter.search(text, true); + } else if (mediaPages[0].selectedType == TAB_GROUPUSERS) { + if (groupUsersSearchAdapter == null) { + return; + } + groupUsersSearchAdapter.search(text, true); + } else if (mediaPages[0].selectedType == TAB_SAVED_DIALOGS) { + if (savedMessagesSearchAdapter == null) { + return; + } + savedMessagesSearchAdapter.search(text, searchingReaction); } - savedMessagesSearchAdapter.search(text, searchingReaction); } - } - @Override - public void onSearchPressed(EditText editText) { - super.onSearchPressed(editText); - if (savedMessagesContainer != null) { - savedMessagesContainer.chatActivity.hitSearch(); + @Override + public void onSearchPressed(EditText editText) { + super.onSearchPressed(editText); + if (savedMessagesContainer != null) { + savedMessagesContainer.chatActivity.hitSearch(); + } } - } - @Override - public void onLayout(int l, int t, int r, int b) { - View parent = (View) searchItem.getParent(); - searchItem.setTranslationX(parent.getMeasuredWidth() - searchItem.getRight()); - } - }); - searchItem.setTranslationY(dp(10)); - searchItem.setSearchFieldHint(getString(searchTagsList != null && searchTagsList.hasFilters() && getSelectedTab() == TAB_SAVED_DIALOGS ? R.string.SavedTagSearchHint : R.string.Search)); - searchItem.setContentDescription(getString("Search", R.string.Search)); - searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); + @Override + public void onLayout(int l, int t, int r, int b) { + View parent = (View) searchItem.getParent(); + searchItem.setTranslationX(parent.getMeasuredWidth() - searchItem.getRight()); + } + }); + searchItem.setTranslationY(dp(10)); + searchItem.setSearchFieldHint(getString(searchTagsList != null && searchTagsList.hasFilters() && getSelectedTab() == TAB_SAVED_DIALOGS ? R.string.SavedTagSearchHint : R.string.Search)); + searchItem.setContentDescription(getString("Search", R.string.Search)); + searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); + } photoVideoOptionsItem = new ImageView(context); photoVideoOptionsItem.setContentDescription(getString("AccDescrMoreOptions", R.string.AccDescrMoreOptions)); @@ -1953,10 +1961,12 @@ public void onClick(View view) { } }); - EditTextBoldCursor editText = searchItem.getSearchField(); - editText.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); - editText.setHintTextColor(getThemedColor(Theme.key_player_time)); - editText.setCursorColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); + if (searchItem != null) { + EditTextBoldCursor editText = searchItem.getSearchField(); + editText.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); + editText.setHintTextColor(getThemedColor(Theme.key_player_time)); + editText.setCursorColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); + } searchItemState = 0; SizeNotifierFrameLayout sizeNotifierFrameLayout = null; @@ -2284,7 +2294,12 @@ public int getStartedTrackingX() { } }; } else if (profileActivity instanceof ProfileActivity) { - giftsContainer = new ProfileGiftsContainer(context, profileActivity.getCurrentAccount(), ((ProfileActivity) profileActivity).getDialogId(), resourcesProvider); + giftsContainer = new ProfileGiftsContainer(context, profileActivity.getCurrentAccount(), ((ProfileActivity) profileActivity).getDialogId(), resourcesProvider) { + @Override + protected int processColor(int color) { + return SharedMediaLayout.this.processColor(color); + } + }; } setWillNotDraw(false); @@ -3069,6 +3084,7 @@ protected void onDraw(Canvas canvas) { searchTagsList = new SearchTagsList(getContext(), profileActivity, null, profileActivity.getCurrentAccount(), includeSavedDialogs() ? 0 : dialog_id, resourcesProvider, false) { @Override protected boolean setFilter(ReactionsLayoutInBubble.VisibleReaction reaction) { + if (searchItem == null) return false; searchingReaction = reaction; final String text = searchItem.getSearchField().getText().toString(); searchWas = text.length() != 0 || searchingReaction != null; @@ -3745,7 +3761,7 @@ public void onPageScrolled(float progress) { optionsAlpha = getPhotoVideoOptionsAlpha(progress); photoVideoOptionsItem.setVisibility((optionsAlpha == 0 || !canShowSearchItem() || isArchivedOnlyStoriesView()) ? INVISIBLE : View.VISIBLE); - if (!canShowSearchItem()) { + if (searchItem != null && !canShowSearchItem()) { searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); searchAlpha = 0.0f; } else { @@ -3758,7 +3774,7 @@ public void onPageScrolled(float progress) { mediaPages[0] = mediaPages[1]; mediaPages[1] = tempPage; mediaPages[1].setVisibility(View.GONE); - if (searchItemState == 2) { + if (searchItem != null && searchItemState == 2) { searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); } searchItemState = 0; @@ -4651,7 +4667,7 @@ private boolean prepareForMoving(MotionEvent ev, boolean forward) { if (id < 0) { return false; } - if (!canShowSearchItem()) { + if (searchItem != null && !canShowSearchItem()) { searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); searchAlpha = 0; } else { @@ -4947,7 +4963,7 @@ public void onAnimationEnd(Animator animator) { tabsAnimation = null; if (backAnimation) { mediaPages[1].setVisibility(View.GONE); - if (!canShowSearchItem()) { + if (searchItem != null && !canShowSearchItem()) { searchItem.setVisibility(isStoriesView() ? View.GONE : INVISIBLE); searchAlpha = 0; } else { @@ -4961,7 +4977,7 @@ public void onAnimationEnd(Animator animator) { mediaPages[0] = mediaPages[1]; mediaPages[1] = tempPage; mediaPages[1].setVisibility(View.GONE); - if (searchItemState == 2) { + if (searchItem != null && searchItemState == 2) { searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); } searchItemState = 0; @@ -5465,6 +5481,9 @@ public void didReceivedNotification(int id, int account, Object... args) { } if (page != null) { AndroidUtilities.notifyDataSetChanged(page.listView); + if (page.listView.getLayoutManager() instanceof LinearLayoutManager) { + checkLoadMoreScroll(page, page.listView, (LinearLayoutManager) page.listView.getLayoutManager()); + } } if (delegate != null) { delegate.updateSelectedMediaTabText(); @@ -5478,6 +5497,9 @@ public void didReceivedNotification(int id, int account, Object... args) { } if (page != null) { AndroidUtilities.notifyDataSetChanged(page.listView); + if (page.listView.getLayoutManager() instanceof LinearLayoutManager) { + checkLoadMoreScroll(page, page.listView, (LinearLayoutManager) page.listView.getLayoutManager()); + } } if (delegate != null) { delegate.updateSelectedMediaTabText(); @@ -6094,7 +6116,7 @@ private void switchToCurrentSelectedMode(boolean animated) { switchToCurrentSelectedMode(true); return; } else { - String text = searchItem.getSearchField().getText().toString(); + String text = searchItem != null ? searchItem.getSearchField().getText().toString() : ""; if (mediaPages[a].selectedType == TAB_FILES) { if (documentsSearchAdapter != null) { documentsSearchAdapter.search(text, false); @@ -6316,16 +6338,18 @@ private void switchToCurrentSelectedMode(boolean animated) { if (giftsContainer != null && mediaPages[a].selectedType != TAB_GIFTS && giftsContainer.getParent() == mediaPages[a]) { mediaPages[a].removeView(giftsContainer); } - if (mediaPages[a].selectedType == TAB_PHOTOVIDEO || mediaPages[a].selectedType == TAB_SAVED_DIALOGS || mediaPages[a].selectedType == TAB_STORIES || mediaPages[a].selectedType == TAB_ARCHIVED_STORIES || mediaPages[a].selectedType == TAB_VOICE || mediaPages[a].selectedType == TAB_GIF || mediaPages[a].selectedType == TAB_COMMON_GROUPS || mediaPages[a].selectedType == TAB_GROUPUSERS && !delegate.canSearchMembers() || mediaPages[a].selectedType == TAB_RECOMMENDED_CHANNELS || mediaPages[a].selectedType == TAB_BOT_PREVIEWS) { + if (mediaPages[a].selectedType == TAB_PHOTOVIDEO || mediaPages[a].selectedType == TAB_SAVED_DIALOGS || mediaPages[a].selectedType == TAB_STORIES || mediaPages[a].selectedType == TAB_ARCHIVED_STORIES || mediaPages[a].selectedType == TAB_VOICE || mediaPages[a].selectedType == TAB_GIF || mediaPages[a].selectedType == TAB_COMMON_GROUPS || mediaPages[a].selectedType == TAB_GROUPUSERS && !delegate.canSearchMembers() || mediaPages[a].selectedType == TAB_RECOMMENDED_CHANNELS || mediaPages[a].selectedType == TAB_BOT_PREVIEWS || mediaPages[a].selectedType == TAB_GIFTS) { if (animated) { searchItemState = 2; } else { searchItemState = 0; - searchItem.setVisibility(isStoriesView() || searching ? View.GONE : View.INVISIBLE); + if (searchItem != null) { + searchItem.setVisibility(isStoriesView() || searching ? View.GONE : View.INVISIBLE); + } } } else { if (animated) { - if (searchItem.getVisibility() == View.INVISIBLE && !actionBar.isSearchFieldVisible()) { + if (searchItem != null && searchItem.getVisibility() == View.INVISIBLE && !actionBar.isSearchFieldVisible()) { if (canShowSearchItem()) { searchItemState = 1; searchItem.setVisibility(View.VISIBLE); @@ -6338,7 +6362,7 @@ private void switchToCurrentSelectedMode(boolean animated) { searchItemState = 0; searchAlpha = 1f; } - } else if (searchItem.getVisibility() == View.INVISIBLE) { + } else if (searchItem != null && searchItem.getVisibility() == View.INVISIBLE) { if (canShowSearchItem()) { searchItemState = 0; searchAlpha = 1; @@ -6360,12 +6384,12 @@ private void switchToCurrentSelectedMode(boolean animated) { StoriesController.StoriesList storiesList = storiesAdapter.storiesList; storiesAdapter.load(false); mediaPages[a].emptyView.showProgress(storiesList != null && (storiesList.isLoading() || hasInternet() && storiesList.getCount() > 0), animated); - fastScrollVisible = storiesList != null && storiesList.getCount() > 0; + fastScrollVisible = storiesList != null && storiesList.getCount() > 0 && !isSearchingStories(); } else if (mediaPages[a].selectedType == TAB_ARCHIVED_STORIES) { StoriesController.StoriesList storiesList = archivedStoriesAdapter.storiesList; archivedStoriesAdapter.load(false); mediaPages[a].emptyView.showProgress(storiesList != null && (storiesList.isLoading() || hasInternet() && storiesList.getCount() > 0), animated); - fastScrollVisible = storiesList != null && storiesList.getCount() > 0; + fastScrollVisible = storiesList != null && storiesList.getCount() > 0 && !isSearchingStories(); } else if (mediaPages[a].selectedType == TAB_RECOMMENDED_CHANNELS) { } else if (mediaPages[a].selectedType == TAB_SAVED_DIALOGS) { @@ -6425,8 +6449,8 @@ private void switchToCurrentSelectedMode(boolean animated) { } else { mediaPages[a].emptyView.stickerView.setVisibility(View.VISIBLE); mediaPages[a].emptyView.setStickerType(StickerEmptyView.STICKER_TYPE_SEARCH); - mediaPages[a].emptyView.title.setText(getString("NoResult", R.string.NoResult)); - mediaPages[a].emptyView.subtitle.setText(getString("SearchEmptyViewFilteredSubtitle2", R.string.SearchEmptyViewFilteredSubtitle2)); + mediaPages[a].emptyView.title.setText(getString(R.string.NoResult)); + mediaPages[a].emptyView.subtitle.setText(getString(R.string.SearchEmptyViewFilteredSubtitle2)); mediaPages[a].emptyView.button.setVisibility(View.GONE); } mediaPages[a].listView.setVisibility(View.VISIBLE); @@ -6445,7 +6469,9 @@ private void switchToCurrentSelectedMode(boolean animated) { actionBar.closeSearchField(); searchItemState = 0; searchAlpha = 0; - searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); + if (searchItem != null) { + searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); + } updateOptionsSearch(); } } @@ -8769,14 +8795,21 @@ public String getBotPreviewsSubtitle(boolean edit) { return sb.toString(); } - private StoriesController.StoriesList searchStoriesList; + public StoriesController.StoriesList searchStoriesList; + public void updateStoriesList(StoriesController.StoriesList list) { + searchStoriesList = list; + storiesAdapter.storiesList = list; + storiesAdapter.notifyDataSetChanged(); + animationSupportingStoriesAdapter.storiesList = list; + animationSupportingStoriesAdapter.notifyDataSetChanged(); + } public class StoriesAdapter extends SharedPhotoVideoAdapter { private final boolean isArchive; private final ArrayList uploadingStories = new ArrayList<>(); @Nullable - public final StoriesController.StoriesList storiesList; + public StoriesController.StoriesList storiesList; private StoriesAdapter supportingAdapter; private int id; @@ -8788,7 +8821,7 @@ public StoriesAdapter(Context context, boolean isArchive) { final int currentAccount = profileActivity.getCurrentAccount(); if (!TextUtils.isEmpty(getStoriesHashtag())) { if (searchStoriesList == null) { - searchStoriesList = new StoriesController.SearchStoriesList(currentAccount, getStoriesHashtag()); + searchStoriesList = new StoriesController.SearchStoriesList(currentAccount, TextUtils.isEmpty(getStoriesHashtagUsername()) ? null : getStoriesHashtagUsername(), getStoriesHashtag()); } storiesList = searchStoriesList; } else if (getStoriesArea() != null) { @@ -9902,6 +9935,10 @@ public String getStoriesHashtag() { return null; } + public String getStoriesHashtagUsername() { + return null; + } + public TL_stories.MediaArea getStoriesArea() { return null; } @@ -10249,4 +10286,8 @@ public boolean drawChild(Canvas canvas, View child, long drawingTime) { return super.drawChild(canvas, child, drawingTime); } }; + + public boolean addActionButtons() { + return true; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java index 2cc923841e2..a85548d02db 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersAlert.java @@ -251,7 +251,7 @@ public void deleteSticker(TLRPC.Document document) { MediaDataController.getInstance(UserConfig.selectedAccount).toggleStickerSet(null, response, 0, null, false, false); } else { stickerSet = (TLRPC.TL_messages_stickerSet) response; - loadStickerSet(); + loadStickerSet(false); updateFields(); } } @@ -430,7 +430,7 @@ public StickersAlert(Context context, Object parentObject, TLObject object, Them inputStickerSet = new TLRPC.TL_inputStickerSetID(); inputStickerSet.id = set.set.id; inputStickerSet.access_hash = set.set.access_hash; - loadStickerSet(); + loadStickerSet(false); } else { stickerSetCovereds = new ArrayList<>(); for (int a = 0; a < vector.objects.size(); a++) { @@ -468,7 +468,7 @@ public StickersAlert(Context context, TLRPC.Vector vector, Theme.ResourcesProvid inputStickerSet = new TLRPC.TL_inputStickerSetID(); inputStickerSet.id = set.set.id; inputStickerSet.access_hash = set.set.access_hash; - loadStickerSet(); + loadStickerSet(false); init(context); } else { stickerSetCovereds = new ArrayList<>(); @@ -560,18 +560,18 @@ public StickersAlert(Context context, String software, ArrayList uri init(context); } - public StickersAlert(Context context, BaseFragment baseFragment, TLRPC.InputStickerSet set, TLRPC.TL_messages_stickerSet loadedSet, StickersAlertDelegate stickersAlertDelegate) { - this(context, baseFragment, set, loadedSet, stickersAlertDelegate, null); + public StickersAlert(Context context, BaseFragment baseFragment, TLRPC.InputStickerSet set, TLRPC.TL_messages_stickerSet loadedSet, StickersAlertDelegate stickersAlertDelegate, boolean forceRequest) { + this(context, baseFragment, set, loadedSet, stickersAlertDelegate, null, forceRequest); } - public StickersAlert(Context context, BaseFragment baseFragment, TLRPC.InputStickerSet set, TLRPC.TL_messages_stickerSet loadedSet, StickersAlertDelegate stickersAlertDelegate, Theme.ResourcesProvider resourcesProvider) { + public StickersAlert(Context context, BaseFragment baseFragment, TLRPC.InputStickerSet set, TLRPC.TL_messages_stickerSet loadedSet, StickersAlertDelegate stickersAlertDelegate, Theme.ResourcesProvider resourcesProvider, boolean forceRequest) { super(context, false, resourcesProvider); fixNavigationBar(); delegate = stickersAlertDelegate; inputStickerSet = set; stickerSet = loadedSet; parentFragment = baseFragment; - loadStickerSet(); + loadStickerSet(forceRequest); init(context); } @@ -583,14 +583,16 @@ public boolean isClearsInputField() { return clearsInputField; } - public void loadStickerSet() { + public void loadStickerSet(boolean force) { if (inputStickerSet != null) { final MediaDataController mediaDataController = MediaDataController.getInstance(currentAccount); - if (stickerSet == null && inputStickerSet.short_name != null) { - stickerSet = mediaDataController.getStickerSetByName(inputStickerSet.short_name); - } - if (stickerSet == null) { - stickerSet = mediaDataController.getStickerSetById(inputStickerSet.id); + if (!force) { + if (stickerSet == null && inputStickerSet.short_name != null) { + stickerSet = mediaDataController.getStickerSetByName(inputStickerSet.short_name); + } + if (stickerSet == null) { + stickerSet = mediaDataController.getStickerSetById(inputStickerSet.id); + } } if (stickerSet == null) { TLRPC.TL_messages_getStickerSet req = new TLRPC.TL_messages_getStickerSet(); @@ -1020,7 +1022,7 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { TLRPC.TL_inputStickerSetID inputStickerSetID = new TLRPC.TL_inputStickerSetID(); inputStickerSetID.access_hash = pack.set.access_hash; inputStickerSetID.id = pack.set.id; - StickersAlert alert = new StickersAlert(parentActivity, parentFragment, inputStickerSetID, null, null, resourcesProvider); + StickersAlert alert = new StickersAlert(parentActivity, parentFragment, inputStickerSetID, null, null, resourcesProvider, false); if (masterDismissListener != null) { alert.setOnDismissListener(di -> masterDismissListener.run()); } @@ -2075,7 +2077,7 @@ public void didReceivedNotification(int id, int account, Object... args) { } if (newStickerSet != null && newStickerSet != stickerSet) { stickerSet = newStickerSet; - loadStickerSet(); + loadStickerSet(false); } updateFields(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersDialogs.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersDialogs.java index ae2637dd0bf..16894bf40c2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersDialogs.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickersDialogs.java @@ -231,6 +231,7 @@ private static void openStickerPickerDialog(TLRPC.TL_messages_stickerSet sticker } public static void showAddStickerDialog(TLRPC.TL_messages_stickerSet stickerSet, View view, BaseFragment fragment, Theme.ResourcesProvider resourcesProvider) { + if (fragment == null) return; Context context = fragment.getContext(); if (!(fragment instanceof ChatActivity)) { openStickerPickerDialog(stickerSet, fragment, resourcesProvider); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/TableView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/TableView.java index 6272372792a..36e5adc86ab 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/TableView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/TableView.java @@ -63,7 +63,7 @@ public TableView(Context context, Theme.ResourcesProvider resourcesProvider) { setColumnStretchable(1, true); } - public void addRow(CharSequence title, View content) { + public TableRow addRow(CharSequence title, View content) { TableRow row = new TableRow(getContext()); TableRow.LayoutParams lp; lp = new TableRow.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT); @@ -71,6 +71,7 @@ public void addRow(CharSequence title, View content) { lp = new TableRow.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f); row.addView(new TableRowContent(this, content), lp); addView(row); + return row; } public TableRow addRowUnpadded(CharSequence title, View content) { @@ -160,7 +161,7 @@ public TableRow addRowDateTime(CharSequence title, int date) { return addRow(title, LocaleController.formatString(R.string.formatDateAtTime, LocaleController.getInstance().getFormatterGiveawayCard().format(new Date(date * 1000L)), LocaleController.getInstance().getFormatterDay().format(new Date(date * 1000L)))); } - public void addRowLink(CharSequence title, CharSequence value, Runnable onClick) { + public TableRow addRowLink(CharSequence title, CharSequence value, Runnable onClick) { final LinkSpanDrawable.LinksTextView textView = new LinkSpanDrawable.LinksTextView(getContext(), resourcesProvider); textView.setPadding(dp(12.66f), dp(9.33f), dp(12.66f), dp(9.33f)); textView.setEllipsize(TextUtils.TruncateAt.END); @@ -183,14 +184,37 @@ public void updateDrawState(@NonNull TextPaint ds) { } }, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); textView.setText(ssb); - addRowUnpadded(title, textView); + return addRowUnpadded(title, textView); } public TableRow addRow(CharSequence title, CharSequence text) { ButtonSpan.TextViewButtons textView = new ButtonSpan.TextViewButtons(getContext()); textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - textView.setText(text); + textView.setText(Emoji.replaceEmoji(text, textView.getPaint().getFontMetricsInt(), false)); + NotificationCenter.listenEmojiLoading(textView); + + TableRow row = new TableRow(getContext()); + TableRow.LayoutParams lp; + lp = new TableRow.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT); + row.addView(new TableRowTitle(this, title), lp); + lp = new TableRow.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f); + row.addView(new TableRowContent(this, textView), lp); + addView(row); + + return row; + } + + public TableRow addRow(CharSequence title, CharSequence text, CharSequence buttonText, Runnable buttonOnClick) { + ButtonSpan.TextViewButtons textView = new ButtonSpan.TextViewButtons(getContext()); + textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + SpannableStringBuilder ssb = new SpannableStringBuilder(Emoji.replaceEmoji(text, textView.getPaint().getFontMetricsInt(), false)); + if (buttonText != null) { + ssb.append(" ").append(ButtonSpan.make(buttonText, buttonOnClick, resourcesProvider)); + } + textView.setText(ssb); + NotificationCenter.listenEmojiLoading(textView); TableRow row = new TableRow(getContext()); TableRow.LayoutParams lp; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Text.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Text.java index 2a8b8f86b25..d95fa6d2bb3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Text.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Text.java @@ -93,8 +93,8 @@ public void setColor(int color) { paint.setColor(color); } - private int ellipsizeWidth = -1; - public Text ellipsize(int width) { + private float ellipsizeWidth = -1; + public Text ellipsize(float width) { ellipsizeWidth = width; return this; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ThanosEffect.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ThanosEffect.java index 1f571407759..d873f23229d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ThanosEffect.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ThanosEffect.java @@ -38,6 +38,7 @@ import org.telegram.ui.ChatActivity; import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicBoolean; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; @@ -74,7 +75,7 @@ public void doFrame(long frameTimeNanos) { private static class ToSet { public final View view; public final ArrayList views; - public final Runnable startCallback, doneCallback; + public Runnable startCallback, doneCallback; public final Bitmap bitmap; public final Matrix matrix; @@ -153,7 +154,7 @@ public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) { if (whenDone != null) { Runnable runnable = whenDone; whenDone = null; - runnable.run(); + ensureRunOnUIThread(runnable); } return false; } @@ -171,7 +172,7 @@ private void destroy() { if (whenDone != null) { Runnable runnable = whenDone; whenDone = null; - runnable.run(); + ensureRunOnUIThread(runnable); } } @@ -182,7 +183,8 @@ public void kill() { destroyed = true; for (ToSet set : toSet) { if (set.doneCallback != null) { - set.doneCallback.run(); + ensureRunOnUIThread(set.doneCallback); + set.doneCallback = null; } } toSet.clear(); @@ -192,7 +194,7 @@ public void kill() { if (whenDone != null) { Runnable runnable = whenDone; whenDone = null; - runnable.run(); + ensureRunOnUIThread(runnable); } } @@ -237,7 +239,8 @@ public void cancel(View view) { ToSet set = toSet.get(i); if (set.view == view) { if (set.doneCallback != null) { - set.doneCallback.run(); + ensureRunOnUIThread(set.doneCallback); + set.doneCallback = null; } toSet.remove(i); i--; @@ -258,10 +261,19 @@ public void animate(Matrix matrix, Bitmap bitmap, Runnable whenStarted, Runnable } } + public static void ensureRunOnUIThread(Runnable runnable) { + if (runnable == null) return; + if (Thread.currentThread() != Looper.getMainLooper().getThread()) { + AndroidUtilities.runOnUIThread(runnable); + } else { + runnable.run(); + } + } + private static class DrawingThread extends DispatchQueue { private boolean isEmulator; - private volatile boolean alive = true; + private AtomicBoolean alive = new AtomicBoolean(true); private final SurfaceTexture surfaceTexture; private final Runnable invalidate; private Runnable destroy; @@ -352,27 +364,27 @@ public void run() { public void requestDraw() { Handler handler = getHandler(); - if (handler != null && alive) { + if (handler != null && alive.get()) { handler.sendMessage(handler.obtainMessage(DO_DRAW)); } } public void resize(int width, int height) { Handler handler = getHandler(); - if (handler != null && alive) { + if (handler != null && alive.get()) { handler.sendMessage(handler.obtainMessage(DO_RESIZE, width, height)); } } public void scroll(int dx, int dy) { Handler handler = getHandler(); - if (handler != null && alive) { + if (handler != null && alive.get()) { handler.sendMessage(handler.obtainMessage(DO_SCROLL, dx, dy)); } } private void resizeInternal(int width, int height) { - if (!alive) { + if (!alive.get()) { return; } this.width = width; @@ -382,7 +394,7 @@ private void resizeInternal(int width, int height) { } public void kill() { - if (!alive) { + if (!alive.get()) { FileLog.d("ThanosEffect: kill failed, already dead"); return; } @@ -396,12 +408,12 @@ public void kill() { } private void killInternal() { - if (!alive) { + if (!alive.get()) { FileLog.d("ThanosEffect: killInternal failed, already dead"); return; } FileLog.d("ThanosEffect: killInternal"); - alive = false; + alive.set(false); for (int i = 0; i < pendingAnimations.size(); ++i) { Animation animation = pendingAnimations.get(i); animation.done(true); @@ -410,14 +422,12 @@ private void killInternal() { if (surfaceTexture != null) { surfaceTexture.release(); } + ensureRunOnUIThread(destroy); + destroy = null; Looper looper = Looper.myLooper(); if (looper != null) { looper.quit(); } - if (destroy != null) { - AndroidUtilities.runOnUIThread(destroy); - destroy = null; - } } private EGL10 egl; @@ -589,7 +599,7 @@ private float animationHeightPart(Animation animation) { private boolean drawnAnimations = false; private void draw() { - if (!alive) return; + if (!alive.get()) return; GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT); @@ -643,7 +653,7 @@ private void draw() { private final ArrayList toAddAnimations = new ArrayList<>(); public void animateGroup(ArrayList views, Runnable whenDone) { - if (!alive) { + if (!alive.get()) { for (int i = 0; i < views.size(); ++i) { views.get(i).setVisibility(GONE); } @@ -661,7 +671,7 @@ public void animateGroup(ArrayList views, Runnable whenDone) { postRunnable(() -> addAnimationInternal(animation)); } public void animate(View view, float durationMultipier, Runnable whenDone) { - if (!alive) { + if (!alive.get()) { if (view != null) { view.setVisibility(GONE); } @@ -681,7 +691,7 @@ public void animate(View view, float durationMultipier, Runnable whenDone) { } public void cancel(View view) { - if (!alive) { + if (!alive.get()) { return; } Handler handler = getHandler(); @@ -690,40 +700,38 @@ public void cancel(View view) { Animation animation = toAddAnimations.get(i); if (animation.views.contains(view)) { if (animation.doneCallback != null) { - animation.doneCallback.run(); + ensureRunOnUIThread(animation.doneCallback); + animation.doneCallback = null; } toAddAnimations.remove(i); i--; } } } else { - for (int i = 0; i < pendingAnimations.size(); ++i) { - Animation a = pendingAnimations.get(i); - if (a.views.contains(view)) { - if (a.doneCallback != null) { - a.doneCallback.run(); - } - break; - } - } +// for (int i = 0; i < pendingAnimations.size(); ++i) { +// Animation a = pendingAnimations.get(i); +// if (a.views.contains(view)) { +// if (a.doneCallback != null) { +// ensureRunOnUIThread(a.doneCallback); +// a.doneCallback = null; +// } +// break; +// } +// } handler.sendMessage(handler.obtainMessage(DO_CANCEL_ANIMATION, view)); } } public void animate(Matrix matrix, Bitmap bitmap, Runnable whenStart, Runnable whenDone) { - if (!alive) { + if (!alive.get()) { AndroidUtilities.runOnUIThread(() -> { - if (whenStart != null) { - whenStart.run(); - } + ensureRunOnUIThread(whenStart); if (whenDone != null) { AndroidUtilities.runOnUIThread(whenDone); } }); - if (destroy != null) { - AndroidUtilities.runOnUIThread(destroy); - destroy = null; - } + ensureRunOnUIThread(destroy); + destroy = null; return; } Animation animation = new Animation(matrix, bitmap, whenStart, whenDone); @@ -1291,11 +1299,8 @@ public void done(boolean runCallback) { try { GLES31.glDeleteTextures(1, texture, 0); } catch (Exception e) { FileLog.e(e); }; if (runCallback && doneCallback != null) { - AndroidUtilities.runOnUIThread(() -> { - if (doneCallback != null) { - doneCallback.run(); - } - }); + ensureRunOnUIThread(doneCallback); + doneCallback = null; } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/TrendingStickersLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/TrendingStickersLayout.java index c78a302240e..ca6f211bc27 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/TrendingStickersLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/TrendingStickersLayout.java @@ -460,7 +460,7 @@ public boolean isInScheduleMode() { } else { stickersAlertDelegate = null; } - final StickersAlert stickersAlert = new StickersAlert(getContext(), parentFragment, inputStickerSet, null, stickersAlertDelegate, resourcesProvider); + final StickersAlert stickersAlert = new StickersAlert(getContext(), parentFragment, inputStickerSet, null, stickersAlertDelegate, resourcesProvider, false); stickersAlert.setShowTooltipWhenToggle(false); stickersAlert.setInstallDelegate(new StickersAlert.StickersAlertInstallDelegate() { @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/UItem.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/UItem.java index 64235687935..8258c2a8645 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/UItem.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/UItem.java @@ -432,6 +432,13 @@ public static UItem asSearchMessage(MessageObject messageObject) { return item; } + public static UItem asSearchMessage(int id, MessageObject messageObject) { + UItem item = new UItem(UniversalAdapter.VIEW_TYPE_SEARCH_MESSAGE, false); + item.id = id; + item.object = messageObject; + return item; + } + public static UItem asFlicker(int type) { UItem item = new UItem(UniversalAdapter.VIEW_TYPE_FLICKER, false); item.intValue = type; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java index da00872c424..70cbef9449a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java @@ -557,13 +557,15 @@ public void showWithAction(ArrayList dialogIds, int action, Object infoObj } else { infoText = AndroidUtilities.replaceTags(LocaleController.formatString("VoipChatUserJoined", R.string.VoipChatUserJoined, UserObject.getFirstName(user))); } - } else { + } else if (infoObject instanceof TLRPC.Chat) { TLRPC.Chat chat = (TLRPC.Chat) infoObject; if (ChatObject.isChannelOrGiga(currentChat)) { infoText = AndroidUtilities.replaceTags(LocaleController.formatString("VoipChannelChatJoined", R.string.VoipChannelChatJoined, chat.title)); } else { infoText = AndroidUtilities.replaceTags(LocaleController.formatString("VoipChatChatJoined", R.string.VoipChatChatJoined, chat.title)); } + } else { + infoText = ""; } subInfoText = null; icon = 0; 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 35d1ace763e..6627c6f4ac4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoPlayer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoPlayer.java @@ -25,6 +25,7 @@ import android.os.Looper; import android.text.TextUtils; import android.util.Base64; +import android.util.Log; import android.util.LongSparseArray; import android.view.Surface; import android.view.SurfaceView; @@ -44,6 +45,7 @@ import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.SeekParameters; +import com.google.android.exoplayer2.Tracks; import com.google.android.exoplayer2.analytics.AnalyticsListener; import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.audio.AudioCapabilities; @@ -51,6 +53,7 @@ import com.google.android.exoplayer2.audio.AudioSink; import com.google.android.exoplayer2.audio.DefaultAudioSink; import com.google.android.exoplayer2.audio.TeeAudioProcessor; +import com.google.android.exoplayer2.mediacodec.MediaCodecDecoderException; import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer; import com.google.android.exoplayer2.mediacodec.MediaCodecUtil; import com.google.android.exoplayer2.source.LoopingMediaSource; @@ -74,6 +77,7 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.BuildVars; import org.telegram.messenger.DispatchQueue; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; @@ -94,13 +98,17 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; -import java.util.List; @SuppressLint("NewApi") public class VideoPlayer implements Player.Listener, VideoListener, AnalyticsListener, NotificationCenter.NotificationCenterDelegate { + private static int lastPlayerId = 0; + private int playerId = lastPlayerId++; + public static final HashSet activePlayers = new HashSet<>(); + private DispatchQueue workerQueue; private boolean isStory; @@ -314,6 +322,7 @@ public void preparePlayerLoop(Uri videoUri, String videoType, Uri audioUri, Stri player.prepare(); audioPlayer.setMediaSource(mediaSource2, true); audioPlayer.prepare(); + activePlayers.add(playerId); } private MediaSource mediaSourceFromUri(Uri uri, String type) { @@ -354,7 +363,8 @@ public void preparePlayer(Uri uri, String type, int priority) { this.audioUri = null; this.audioType = null; this.loopingMediaSource = false; - currentStreamIsHls = false; + this.autoIsOriginal = false; + this.currentStreamIsHls = false; videoPlayerReady = false; mixedAudio = false; @@ -367,14 +377,15 @@ public void preparePlayer(Uri uri, String type, int priority) { player.prepare(); } - public void preparePlayer(ArrayList uris, Quality select) { - this.videoQualities = uris; + public void preparePlayer(ArrayList qualities, Quality select) { + this.videoQualities = qualities; this.videoQualityToSelect = select; this.videoUri = null; this.videoType = "hls"; this.audioUri = null; this.audioType = null; this.loopingMediaSource = false; + this.autoIsOriginal = false; videoPlayerReady = false; mixedAudio = false; @@ -383,7 +394,11 @@ public void preparePlayer(ArrayList uris, Quality select) { ensurePlayerCreated(); currentStreamIsHls = false; + selectedQualityIndex = select == null || videoQualities == null ? QUALITY_AUTO : videoQualities.indexOf(select); setSelectedQuality(true, select); + if (autoIsOriginal) { + selectedQualityIndex = QUALITY_AUTO; + } } public static Quality getSavedQuality(ArrayList qualities, MessageObject messageObject) { @@ -418,7 +433,23 @@ public static void saveQuality(Quality q, long did, int mid) { editor.apply(); } + public static void saveLooping(boolean looping, MessageObject messageObject) { + if (messageObject == null) return; + final SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("media_saved_pos", Activity.MODE_PRIVATE); + final String key = messageObject.getDialogId() + "_" + messageObject.getId() + "loop"; + preferences.edit().putBoolean(key, looping).apply(); + } + + public static Boolean getLooping(MessageObject messageObject) { + if (messageObject == null) return null; + final SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("media_saved_pos", Activity.MODE_PRIVATE); + final String key = messageObject.getDialogId() + "_" + messageObject.getId() + "loop"; + if (!preferences.contains(key)) return null; + return preferences.getBoolean(key, false); + } + public static final int QUALITY_AUTO = -1; // HLS + private boolean autoIsOriginal = false; private int selectedQualityIndex = QUALITY_AUTO; private boolean currentStreamIsHls; @@ -428,6 +459,14 @@ public Quality getQuality(int index) { return videoQualities.get(index); } + public Quality getOriginalQuality() { + for (int i = 0; i < getQualitiesCount(); ++i) { + final Quality q = getQuality(i); + if (q.original) return q; + } + return null; + } + public Quality getHighestQuality(Boolean original) { Quality max = null; for (int i = 0; i < getQualitiesCount(); ++i) { @@ -470,50 +509,122 @@ public int getQualitiesCount() { return videoQualities.size(); } + public File getFile() { + if (videoQualities != null) { + for (Quality q : videoQualities) { + for (VideoUri v : q.uris) { + if (v.isCached()) + return new File(v.uri.getPath()); + } + } + } + if (videoUri != null) { + if ("file".equalsIgnoreCase(videoUri.getScheme())) + return new File(videoUri.getPath()); + } + return null; + } + + public File getLowestFile() { + if (videoQualities != null) { + for (int i = videoQualities.size() - 1; i >= 0; --i) { + Quality q = videoQualities.get(i); + for (VideoUri v : q.uris) { + if (!v.isCached()) + v.updateCached(true); + if (v.isCached()) + return new File(v.uri.getPath()); + } + } + } + if (videoUri != null) { + if ("file".equalsIgnoreCase(videoUri.getScheme())) + return new File(videoUri.getPath()); + + } + return null; + } + public int getSelectedQuality() { return selectedQualityIndex; } + public TLRPC.Document getCurrentDocument() { + if (player == null) return null; + final Format format = player.getVideoFormat(); + if (format == null || format.documentId == 0) + return null; + if (videoQualities != null) { + for (Quality q : videoQualities) { + for (VideoUri u : q.uris) { + if (u.docId == format.documentId) + return u.document; + } + } + } + return null; + } + public int getCurrentQualityIndex() { if (selectedQualityIndex == QUALITY_AUTO) { try { - final MappingTrackSelector.MappedTrackInfo mapTrackInfo = trackSelector.getCurrentMappedTrackInfo(); - for (int renderIndex = 0; renderIndex < mapTrackInfo.getRendererCount(); ++renderIndex) { - final TrackGroupArray trackGroups = mapTrackInfo.getTrackGroups(renderIndex); - for (int groupIndex = 0; groupIndex < trackGroups.length; ++groupIndex) { - final TrackGroup trackGroup = trackGroups.get(groupIndex); - for (int trackIndex = 0; trackIndex < trackGroup.length; ++trackIndex) { - final Format format = trackGroup.getFormat(trackIndex); - int formatIndex; - try { - formatIndex = Integer.parseInt(format.id); - } catch (Exception e) { - formatIndex = -1; - } - if (formatIndex >= 0) { - int formatOrder = 0; - for (int j = 0; j < getQualitiesCount(); ++j) { - final Quality q = getQuality(j); - for (int i = 0; i < q.uris.size(); ++i){ - if (q.uris.get(i).m3u8uri != null) { - if (formatOrder == formatIndex) { - return j; - } - formatOrder++; - } - } - } - } - for (int j = 0; j < getQualitiesCount(); ++j) { - final Quality q = getQuality(j); - if (format.width == q.width && format.height == q.height) { - return j; - } - } + if (autoIsOriginal) { + for (int j = 0; j < getQualitiesCount(); ++j) { + final Quality q = getQuality(j); + if (q.original) { + return j; } } } + + if (player == null) return -1; + final Format format = player.getVideoFormat(); + if (format == null) return -1; + for (int j = 0; j < getQualitiesCount(); ++j) { + final Quality q = getQuality(j); + if (!q.original && format.width == q.width && format.height == q.height && format.bitrate == (int) Math.floor(q.uris.get(0).bitrate * 8)) { + return j; + } + } + +// final MappingTrackSelector.MappedTrackInfo mapTrackInfo = trackSelector.getCurrentMappedTrackInfo(); +// for (int renderIndex = 0; renderIndex < mapTrackInfo.getRendererCount(); ++renderIndex) { +// final TrackGroupArray trackGroups = mapTrackInfo.getTrackGroups(renderIndex); +// for (int groupIndex = 0; groupIndex < trackGroups.length; ++groupIndex) { +// final TrackGroup trackGroup = trackGroups.get(groupIndex); +// for (int trackIndex = 0; trackIndex < trackGroup.length; ++trackIndex) { +// final Format format = trackGroup.getFormat(trackIndex); +// int formatIndex; +// try { +// formatIndex = Integer.parseInt(format.id); +// } catch (Exception e) { +// formatIndex = -1; +// } +// if (formatIndex >= 0) { +// int formatOrder = 0; +// for (int j = 0; j < getQualitiesCount(); ++j) { +// final Quality q = getQuality(j); +// for (int i = 0; i < q.uris.size(); ++i){ +// if (q.uris.get(i).m3u8uri != null) { +// if (formatOrder == formatIndex) { +// return j; +// } +// formatOrder++; +// } +// } +// } +// } +// for (int j = 0; j < getQualitiesCount(); ++j) { +// final Quality q = getQuality(j); +// if (format.width == q.width && format.height == q.height) { +// return j; +// } +// } +// } +// } +// } } catch (Exception e) { + FileLog.e(e); return -1; } } @@ -590,7 +701,16 @@ private void setSelectedQuality(boolean start, Quality quality) { videoQualityToSelect = quality; if (quality == null) { // AUTO final Uri hlsManifest = makeManifest(videoQualities); - if (hlsManifest != null) { + final Quality original = getOriginalQuality(); + if (original != null && original.uris.size() == 1 && original.uris.get(0).isCached()) { + currentStreamIsHls = false; + autoIsOriginal = true; + quality = original; + videoQualityToSelect = quality; + player.setMediaSource(mediaSourceFromUri(quality.getDownloadUri().uri, "other"), false); + reset = true; + } else if (hlsManifest != null) { + autoIsOriginal = false; trackSelector.setParameters(trackSelector.getParameters().buildUpon().clearOverrides().build()); if (!currentStreamIsHls) { currentStreamIsHls = true; @@ -603,18 +723,20 @@ private void setSelectedQuality(boolean start, Quality quality) { if (quality == null || quality.uris.isEmpty()) return; currentStreamIsHls = false; videoQualityToSelect = quality; - player.setMediaSource(mediaSourceFromUri(quality.uris.get(0).uri, "other"), false); + autoIsOriginal = quality.original; + player.setMediaSource(mediaSourceFromUri(quality.getDownloadUri().uri, "other"), false); reset = true; } } else { + autoIsOriginal = false; if (quality.uris.isEmpty()) return; Uri hlsManifest = null; if (quality.uris.size() > 1) { hlsManifest = makeManifest(videoQualities); } - if (hlsManifest == null || quality.uris.size() == 1) { + if (hlsManifest == null || quality.uris.size() == 1 || trackSelector.getCurrentMappedTrackInfo() == null) { currentStreamIsHls = false; - player.setMediaSource(mediaSourceFromUri(quality.uris.get(0).uri, "other"), false); + player.setMediaSource(mediaSourceFromUri(quality.getDownloadUri().uri, "other"), false); reset = true; } else { if (!currentStreamIsHls) { @@ -640,6 +762,10 @@ private void setSelectedQuality(boolean start, Quality quality) { player.play(); } } + if (onQualityChangeListener != null) { + AndroidUtilities.runOnUIThread(onQualityChangeListener); + } + activePlayers.add(playerId); } } @@ -704,11 +830,11 @@ public static ArrayList getQualities(int currentAccount, TLRPC.Document final VideoUri q = result.get(i); if (q.codec != null) { if (forThumb) { - if (!("avc".equals(q.codec) || "h264".equals(q.codec) || "h265".equals(q.codec) || "hevc".equals(q.codec) || "vp9".equals(q.codec) || "vp8".equals(q.codec))) { + if (!("avc".equals(q.codec) || "h264".equals(q.codec) || "vp9".equals(q.codec) || "vp8".equals(q.codec) || ("av1".equals(q.codec) || "av01".equals(q.codec)) && supportsHardwareDecoder(q.codec))) { continue; } } else { - if (("av1".equals(q.codec) || "hevc".equals(q.codec) || "vp9".equals(q.codec)) && !supportsHardwareDecoder(q.codec)) { + if (("av1".equals(q.codec) || "av01".equals(q.codec) || "hevc".equals(q.codec) || "h265".equals(q.codec) || "vp9".equals(q.codec)) && !supportsHardwareDecoder(q.codec)) { continue; } } @@ -722,7 +848,13 @@ public static ArrayList getQualities(int currentAccount, TLRPC.Document else qualities.addAll(filtered); - return Quality.groupBy(qualities); + return Quality.group(qualities); + } + + public static ArrayList getQualities(int currentAccount, TLRPC.MessageMedia media) { + if (!(media instanceof TLRPC.TL_messageMediaDocument)) + return new ArrayList<>(); + return getQualities(currentAccount, media.document, media.alt_documents, 0, false); } public static boolean hasQualities(int currentAccount, TLRPC.MessageMedia media) { @@ -735,12 +867,23 @@ public static boolean hasQualities(int currentAccount, TLRPC.MessageMedia media) public static TLRPC.Document getDocumentForThumb(int currentAccount, TLRPC.MessageMedia media) { if (!(media instanceof TLRPC.TL_messageMediaDocument)) return null; - ArrayList qualities = getQualities(currentAccount, media.document, media.alt_documents, 0, true); - final int MAX_SIZE = 860; + final VideoUri videoUri = getQualityForThumb(getQualities(currentAccount, media.document, media.alt_documents, 0, true)); + return videoUri == null ? null : videoUri.document; + } + + public static VideoUri getQualityForThumb(ArrayList qualities) { + for (final Quality q : qualities) { + for (final VideoUri v : q.uris) { + if (v.isCached()) + return v; + } + } + + final int MAX_SIZE = 900; VideoUri uri = null; for (final Quality q : qualities) { for (final VideoUri v : q.uris) { - if ((uri == null || uri.width * uri.height < v.width * v.height) && v.width <= MAX_SIZE && v.height <= MAX_SIZE) { + if (!v.original && (uri == null || uri.width * uri.height > v.width * v.height || v.bitrate < uri.bitrate) && v.width <= MAX_SIZE && v.height <= MAX_SIZE) { uri = v; } } @@ -748,38 +891,84 @@ public static TLRPC.Document getDocumentForThumb(int currentAccount, TLRPC.Messa if (uri == null) { for (final Quality q : qualities) { for (final VideoUri v : q.uris) { - if ((uri == null || uri.width * uri.height > v.width * v.height)){ + if ((uri == null || uri.width * uri.height > v.width * v.height || v.bitrate < uri.bitrate)) { + uri = v; + } + } + } + } + return uri; + } + + public static VideoUri getQualityForPlayer(ArrayList qualities) { + for (final Quality q : qualities) { + for (final VideoUri v : q.uris) { + if (v.original && v.isCached()) + return v; + } + } + + VideoUri uri = null; + if (uri == null) { + for (final Quality q : qualities) { + for (final VideoUri v : q.uris) { + if (!v.original && VideoPlayer.supportsHardwareDecoder(v.codec) && (uri == null || v.width * v.height > uri.width * uri.height || v.width * v.height == uri.width * uri.height && v.bitrate < uri.bitrate)) { + uri = v; + } + } + } + } + if (uri == null) { + for (final Quality q : qualities) { + for (final VideoUri v : q.uris) { + if (uri == null || uri.width * uri.height > v.width * v.height || v.bitrate < uri.bitrate) { uri = v; } } } } - return uri == null ? null : uri.document; + return uri; } + public static String toMime(String codec) { + if (codec == null) return null; + switch (codec) { + case "h264": + case "avc": return "video/avc"; + case "vp8": return "video/x-vnd.on2.vp8"; + case "vp9": return "video/x-vnd.on2.vp9"; + case "h265": + case "hevc": return "video/hevc"; + case "av1": case "av01": return "video/av01"; + default: return "video/" + codec; + } + } + + private static HashMap cachedSupportedCodec; public static boolean supportsHardwareDecoder(String codec) { try { - switch (codec) { - case "h264": - case "avc": codec = "video/avc"; break; - case "vp8": codec = "video/x-vnd.on2.vp8"; break; - case "vp9": codec = "video/x-vnd.on2.vp9"; break; - case "h265": - case "hevc": codec = "video/hevc"; break; - case "av1": case "av01": codec = "video/av01"; break; - default: codec = "video/" + codec; break; + final String mime = toMime(codec); + if (mime == null) return false; + if (cachedSupportedCodec == null) cachedSupportedCodec = new HashMap<>(); + Boolean cached = cachedSupportedCodec.get(mime); + if (cached != null) return cached; + if (MessagesController.getGlobalMainSettings().getBoolean("unsupport_" + mime, false)) { + return false; } final int count = MediaCodecList.getCodecCount(); for (int i = 0; i < count; i++) { final MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); if (info.isEncoder()) continue; - if (!MediaCodecUtil.isHardwareAccelerated(info, codec)) continue; + if (!MediaCodecUtil.isHardwareAccelerated(info, mime)) continue; final String[] supportedTypes = info.getSupportedTypes(); for (int j = 0; j < supportedTypes.length; ++j) { - if (supportedTypes[j].equalsIgnoreCase(codec)) + if (supportedTypes[j].equalsIgnoreCase(mime)) { + cachedSupportedCodec.put(mime, true); return true; + } } } + cachedSupportedCodec.put(mime, false); return false; } catch (Exception e) { FileLog.e(e); @@ -794,20 +983,38 @@ public Uri makeManifest(ArrayList qualities) { sb.append("#EXT-X-INDEPENDENT-SEGMENTS\n\n"); manifestUris = new ArrayList<>(); boolean hasManifests = false; + ArrayList streams = new ArrayList<>(); for (Quality q : qualities) { for (VideoUri v : q.uris) { mediaDataSourceFactory.putDocumentUri(v.docId, v.uri); mediaDataSourceFactory.putDocumentUri(v.manifestDocId, v.m3u8uri); if (v.m3u8uri != null) { manifestUris.add(v); - sb.append("#EXT-X-STREAM-INF:BANDWIDTH=").append((int) Math.floor(v.bitrate * 8)).append(",RESOLUTION=").append(v.width).append("x").append(v.height); - sb.append("\n"); - sb.append("mtproto:").append(v.manifestDocId).append("\n\n"); + final StringBuilder stream = new StringBuilder(); + stream.append("#EXT-X-STREAM-INF:BANDWIDTH=").append((int) Math.floor(v.bitrate * 8)).append(",RESOLUTION=").append(v.width).append("x").append(v.height); + final String mime = toMime(v.codec); + if (mime != null) { + stream.append(",MIME=\"").append(mime).append("\""); + } + if (v.isCached() && v.isManifestCached()) { + stream.append(",CACHED=\"true\""); + } + stream.append(",DOCID=\"").append(v.docId).append("\""); + stream.append(",ACCOUNT=\"").append(v.currentAccount).append("\""); + stream.append("\n"); + if (v.isManifestCached()) { + stream.append(v.m3u8uri).append("\n\n"); + } else { + stream.append("mtproto:").append(v.manifestDocId).append("\n\n"); + } hasManifests = true; + streams.add(stream.toString()); } } } if (!hasManifests) return null; + Collections.reverse(streams); + sb.append(TextUtils.join("", streams)); final String base64 = Base64.encodeToString(sb.toString().getBytes(), Base64.NO_WRAP); return Uri.parse("data:application/x-mpegurl;base64," + base64); } @@ -825,7 +1032,7 @@ public Quality(VideoUri uri) { uris.add(uri); } - public static ArrayList groupBy(ArrayList uris) { + public static ArrayList group(ArrayList uris) { final ArrayList qualities = new ArrayList<>(); for (VideoUri uri : uris) { @@ -849,6 +1056,35 @@ public static ArrayList groupBy(ArrayList uris) { } } + if (BuildVars.LOGS_ENABLED) { + for (Quality q : qualities) { + FileLog.d("debug_loading_player: Quality "+q.p()+"p (" + q.width + "x" + q.height + ")" + (q.original ? " (source)" : "") + ":"); + for (VideoUri uri : q.uris) { + FileLog.d("debug_loading_player: - video " + uri.width + "x" + uri.height + ", codec=" + uri.codec + ", bitrate=" + (int) (uri.bitrate*8) + ", doc#" + uri.docId + (uri.isCached() ? " (cached)" : "") + ", manifest#" + uri.manifestDocId + (uri.isManifestCached() ? " (cached)" : "")); + } + } + FileLog.d("debug_loading_player: "); + } + + return qualities; + } + + public static ArrayList filterByCodec(ArrayList qualities) { + if (qualities == null) return null; + for (int i = 0; i < qualities.size(); ++i) { + Quality q = qualities.get(i); + for (int j = 0; j < q.uris.size(); ++j) { + VideoUri u = q.uris.get(j); + if (!TextUtils.isEmpty(u.codec) && !supportsHardwareDecoder(u.codec)) { + q.uris.remove(j); + j--; + } + } + if (q.uris.isEmpty()) { + qualities.remove(i); + i--; + } + } return qualities; } @@ -861,37 +1097,69 @@ public String toString() { AndroidUtilities.formatFileSize((long) uris.get(0).bitrate).replace(" ", "") + "/s" + (uris.get(0).codec != null ? ", " + uris.get(0).codec : ""); } else { - int p = Math.min(width, height); - if (Math.abs(p - 1080) < 30) p = 1080; - else if (Math.abs(p - 720) < 30) p = 720; - else if (Math.abs(p - 360) < 30) p = 360; - else if (Math.abs(p - 240) < 30) p = 240; - else if (Math.abs(p - 144) < 30) p = 144; - return p + "p" + (original ? " (" + getString(R.string.QualitySource) + ")" : ""); + return p() + "p" + (original ? " (" + getString(R.string.QualitySource) + ")" : ""); } } - private static final List preferableCodecs_1 = Arrays.asList("h264", "avc"); - private static final ArrayList preferableCodecs_2 = new ArrayList(Arrays.asList("h265", "hevc")); + public int p() { + int p = Math.min(width, height); + if (Math.abs(p - 2160) < 55) p = 2160; + else if (Math.abs(p - 1440) < 55) p = 1440; + else if (Math.abs(p - 1080) < 55) p = 1080; + else if (Math.abs(p - 720) < 55) p = 720; + else if (Math.abs(p - 480) < 55) p = 480; + else if (Math.abs(p - 360) < 55) p = 360; + else if (Math.abs(p - 240) < 55) p = 240; + else if (Math.abs(p - 144) < 55) p = 144; + return p; + } public TLRPC.Document getDownloadDocument() { if (uris.isEmpty()) return null; - for (VideoUri v : uris) { - if (v.codec != null && preferableCodecs_1.contains(v.codec)) { - return v.document; - } + for (VideoUri uri : uris) { + if (uri.isCached()) + return uri.document; } - for (VideoUri v : uris) { - if (v.codec != null && preferableCodecs_2.contains(v.codec)) { - return v.document; + long min_size = Long.MAX_VALUE; + VideoUri selected_uri = null; + for (int i = 0; i < uris.size(); ++i) { + VideoUri uri = uris.get(i); + if (uri.size < min_size && supportsHardwareDecoder(uri.codec)) { + min_size = uri.size; + selected_uri = uri; } } + if (selected_uri != null) { + return selected_uri.document; + } return uris.get(0).document; } + + public VideoUri getDownloadUri() { + if (uris.isEmpty()) return null; + for (VideoUri uri : uris) { + if (uri.isCached()) + return uri; + } + long min_size = Long.MAX_VALUE; + VideoUri selected_uri = null; + for (int i = 0; i < uris.size(); ++i) { + VideoUri uri = uris.get(i); + if (uri.size < min_size && supportsHardwareDecoder(uri.codec)) { + min_size = uri.size; + selected_uri = uri; + } + } + if (selected_uri != null) { + return selected_uri; + } + return uris.get(0); + } } public static class VideoUri { + public int currentAccount; public boolean original; public long docId; public Uri uri; @@ -899,6 +1167,40 @@ public static class VideoUri { public Uri m3u8uri; public TLRPC.Document document; + public TLRPC.Document manifestDocument; + + public boolean isCached() { + return uri != null && "file".equalsIgnoreCase(uri.getScheme()); + } + + public boolean isManifestCached() { + return m3u8uri != null && "file".equalsIgnoreCase(m3u8uri.getScheme()); + } + + public void updateCached(boolean useFileDatabaseQueue) { + if (!isCached() && document != null) { + File file = FileLoader.getInstance(currentAccount).getPathToAttach(document, null, false, useFileDatabaseQueue); + if (file != null && file.exists()) { + this.uri = Uri.fromFile(file); + } else { + file = FileLoader.getInstance(currentAccount).getPathToAttach(document, null, true, useFileDatabaseQueue); + if (file != null && file.exists()) { + this.uri = Uri.fromFile(file); + } + } + } + if (!isManifestCached() && manifestDocument != null) { + File file = FileLoader.getInstance(currentAccount).getPathToAttach(manifestDocument, null, false, useFileDatabaseQueue); + if (file != null && file.exists()) { + this.m3u8uri = Uri.fromFile(file); + } else { + file = FileLoader.getInstance(currentAccount).getPathToAttach(manifestDocument, null, true, useFileDatabaseQueue); + if (file != null && file.exists()) { + this.m3u8uri = Uri.fromFile(file); + } + } + } + } public int width, height; public double duration; @@ -932,17 +1234,24 @@ public static VideoUri of(int currentAccount, TLRPC.Document document, TLRPC.Doc break; } } - final String codec = attributeVideo == null ? null : attributeVideo.video_codec; + final String codec = attributeVideo == null || attributeVideo.video_codec == null ? null : attributeVideo.video_codec.toLowerCase(); + videoUri.currentAccount = currentAccount; videoUri.document = document; videoUri.docId = document.id; videoUri.uri = getUri(currentAccount, document, reference); if (manifest != null) { + videoUri.manifestDocument = manifest; videoUri.manifestDocId = manifest.id; videoUri.m3u8uri = getUri(currentAccount, manifest, reference); File file = FileLoader.getInstance(currentAccount).getPathToAttach(manifest, null, false, true); if (file != null && file.exists()) { -// qualityUri.m3u8uri = Uri.fromFile(file); + videoUri.m3u8uri = Uri.fromFile(file); + } else { + file = FileLoader.getInstance(currentAccount).getPathToAttach(manifest, null, true, true); + if (file != null && file.exists()) { + videoUri.m3u8uri = Uri.fromFile(file); + } } } @@ -958,7 +1267,12 @@ public static VideoUri of(int currentAccount, TLRPC.Document document, TLRPC.Doc File file = FileLoader.getInstance(currentAccount).getPathToAttach(document, null, false, true); if (file != null && file.exists()) { -// qualityUri.uri = Uri.fromFile(file); + videoUri.uri = Uri.fromFile(file); + } else { + file = FileLoader.getInstance(currentAccount).getPathToAttach(document, null, true, true); + if (file != null && file.exists()) { + videoUri.uri = Uri.fromFile(file); + } } return videoUri; @@ -978,6 +1292,7 @@ public boolean isPlayerPrepared() { } public void releasePlayer(boolean async) { + activePlayers.remove(playerId); if (player != null) { player.release(); player = null; @@ -999,11 +1314,17 @@ public void onSeekStarted(EventTime eventTime) { } } + private final ArrayList seekFinishedListeners = new ArrayList<>(); + @Override public void onSeekProcessed(EventTime eventTime) { if (delegate != null) { delegate.onSeekFinished(eventTime); } + for (Runnable r : seekFinishedListeners) { + r.run(); + } + seekFinishedListeners.clear(); } @Override @@ -1181,6 +1502,36 @@ public void seekTo(long positionMs, boolean fast) { } } + public void seekTo(long positionMs, boolean fast, Runnable whenDone) { + if (player != null) { + if (whenDone != null) { + seekFinishedListeners.add(whenDone); + } + player.setSeekParameters(fast ? SeekParameters.CLOSEST_SYNC : SeekParameters.EXACT); + player.seekTo(positionMs); + } + } + + public void seekToBack(long positionMs, boolean fast, Runnable whenDone) { + if (player != null) { + if (whenDone != null) { + seekFinishedListeners.add(whenDone); + } + player.setSeekParameters(fast ? SeekParameters.PREVIOUS_SYNC : SeekParameters.EXACT); + player.seekTo(positionMs); + } + } + + public void seekToForward(long positionMs, boolean fast, Runnable whenDone) { + if (player != null) { + if (whenDone != null) { + seekFinishedListeners.add(whenDone); + } + player.setSeekParameters(fast ? SeekParameters.NEXT_SYNC : SeekParameters.EXACT); + player.seekTo(positionMs); + } + } + public void setDelegate(VideoPlayerDelegate videoPlayerDelegate) { delegate = videoPlayerDelegate; } @@ -1279,6 +1630,21 @@ public void onPositionDiscontinuity(Player.PositionInfo oldPosition, Player.Posi public void onPlayerError(PlaybackException error) { AndroidUtilities.runOnUIThread(() -> { Throwable cause = error.getCause(); + if (cause instanceof MediaCodecDecoderException) { + if (cause.toString().contains("av1") || cause.toString().contains("av01")) { + FileLog.e(error); + FileLog.e("av1 codec failed, we think this codec is not supported"); + MessagesController.getGlobalMainSettings().edit().putBoolean("unsupport_video/av01", true).commit(); + if (cachedSupportedCodec != null) { + cachedSupportedCodec.clear(); + } + videoQualities = Quality.filterByCodec(videoQualities); + if (videoQualities != null) { + preparePlayer(videoQualities, videoQualityToSelect); + } + return; + } + } if (textureView != null && (!triedReinit && cause instanceof MediaCodecRenderer.DecoderInitializationException || cause instanceof SurfaceNotValidException)) { triedReinit = true; if (player != null) { @@ -1407,7 +1773,7 @@ public VisualizerBufferSink() { @Override public void flush(int sampleRateHz, int channelCount, int encoding) { - + } @@ -1559,4 +1925,13 @@ public void setWorkerQueue(DispatchQueue dispatchQueue) { public void setIsStory() { isStory = true; } + + @Override + public void onTracksChanged(Tracks tracks) { + Player.Listener.super.onTracksChanged(tracks); + if (onQualityChangeListener != null) { + AndroidUtilities.runOnUIThread(onQualityChangeListener); + } + } + } 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 6caca2f8cbf..3d8c5d886a5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ViewPagerFixed.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ViewPagerFixed.java @@ -272,6 +272,12 @@ public void onPageSelected(int page, boolean forward) { } } + @Override + public boolean needsTab(int page) { + if (adapter == null) return true; + return adapter.needsTab(page); + } + @Override public void onPageScrolled(float progress) { if (progress == 1f) { @@ -400,7 +406,8 @@ public void fillTabs(boolean animated) { if (adapter != null && tabsView != null) { tabsView.removeTabs(); for (int i = 0; i < adapter.getItemCount(); i++) { - tabsView.addTab(adapter.getItemId(i), adapter.getItemTitle(i)); + if (adapter.needsTab(i)) + tabsView.addTab(adapter.getItemId(i), adapter.getItemTitle(i)); } addMoreTabs(); if (animated) { @@ -424,6 +431,9 @@ private boolean prepareForMoving(MotionEvent ev, boolean forward) { if (forward && !canScrollForward(ev)) { return false; } + if (adapter != null && !adapter.canScrollTo(currentPosition + (forward ? +1 : -1))) { + return false; + } getParent().requestDisallowInterceptTouchEvent(true); maybeStartTracking = false; @@ -970,6 +980,14 @@ public int getItemViewType(int position) { public boolean hasStableId() { return false; } + + public boolean needsTab(int position) { + return true; + } + + public boolean canScrollTo(int position) { + return true; + } } @Override @@ -1007,6 +1025,7 @@ public interface TabsViewDelegate { default void onSamePageSelected() {}; default void invalidateBlur() {}; default boolean canPerformActions() { return true; }; + default boolean needsTab(int page) { return true; } } private static class Tab { @@ -1467,8 +1486,11 @@ public void scrollToTab(int id, int position) { scrollingToChild = -1; previousPosition = currentPosition; previousId = selectedTabId; - currentPosition = position; - selectedTabId = id; + final boolean moveTab = delegate == null || delegate.needsTab(position); + if (moveTab) { + currentPosition = position; + selectedTabId = id; + } if (tabsAnimator != null) { tabsAnimator.cancel(); @@ -1486,7 +1508,7 @@ public void scrollToTab(int id, int position) { if (delegate != null) { delegate.onPageSelected(position, scrollingForward); } - scrollToChild(position); + scrollToChild(currentPosition); tabsAnimator = ValueAnimator.ofFloat(0,1f); tabsAnimator.addUpdateListener(anm -> { float progress = (float) anm.getAnimatedValue(); @@ -1746,7 +1768,11 @@ public void selectTab(int currentPosition, int nextPosition, float progress) { selectedTabId = positionToId.get(currentPosition); if (progress > 0) { - manualScrollingToPosition = nextPosition; + if (delegate == null || delegate.needsTab(nextPosition)) { + manualScrollingToPosition = nextPosition; + } else { + manualScrollingToPosition = currentPosition; + } manualScrollingToId = positionToId.get(nextPosition); } else { manualScrollingToPosition = -1; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/WebPlayerView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/WebPlayerView.java index 1007f9075cf..bfc33facd90 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/WebPlayerView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/WebPlayerView.java @@ -41,6 +41,8 @@ import android.widget.FrameLayout; import android.widget.ImageView; +import androidx.annotation.Keep; + import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; @@ -435,6 +437,7 @@ public JavaScriptInterface(CallJavaResultInterface callJavaResult) { callJavaResultInterface = callJavaResult; } + @Keep @JavascriptInterface public void returnResultToJava(String value) { callJavaResultInterface.jsCallFinished(value); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java index bc74f8cc223..36c734dc4c2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java @@ -183,6 +183,7 @@ import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.BlurredRecyclerView; import org.telegram.ui.Components.PermissionRequest; +import org.telegram.ui.Components.UItem; import org.telegram.ui.Gifts.GiftSheet; import org.telegram.ui.Stars.StarsController; import org.telegram.ui.Stars.StarsIntroActivity; @@ -491,6 +492,7 @@ public void updateList(boolean animated) { @Nullable private ActionBarMenuSubItem blockItem; + private IUpdateButton updateButton; private float additionalFloatingTranslation; private float additionalFloatingTranslation2; private float floatingButtonTranslation; @@ -575,11 +577,6 @@ public void updateList(boolean animated) { public boolean allowBots; private boolean closeFragment; - private FrameLayout updateLayout; - private AnimatorSet updateLayoutAnimator; - private RadialProgress2 updateLayoutIcon; - private TextView updateTextView; - private DialogsActivityDelegate delegate; private ArrayList selectedDialogs = new ArrayList<>(); @@ -4141,12 +4138,12 @@ public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State showPremiumBlockedToast(view, ((DialogCell) view).getDialogId()); return; } - if (initialDialogsType == DIALOGS_TYPE_BOT_REQUEST_PEER && view instanceof TextCell) { - viewPage.dialogsAdapter.onCreateGroupForThisClick(); - return; - } else if (initialDialogsType == DIALOGS_TYPE_WIDGET) { + if (clickSelectsDialog()) { onItemLongClick(viewPage.listView, view, position, 0, 0, viewPage.dialogsType, viewPage.dialogsAdapter); return; + } else if (initialDialogsType == DIALOGS_TYPE_BOT_REQUEST_PEER && view instanceof TextCell) { + viewPage.dialogsAdapter.onCreateGroupForThisClick(); + return; } else if ((initialDialogsType == DIALOGS_TYPE_IMPORT_HISTORY_GROUPS || initialDialogsType == DIALOGS_TYPE_IMPORT_HISTORY) && position == 1) { Bundle args = new Bundle(); args.putBoolean("forImport", true); @@ -4652,7 +4649,7 @@ public void getOutline(View view, Outline outline) { updateDialogsHint(); }); contentView.addView(dialogsHintCell); - } else if (initialDialogsType == DIALOGS_TYPE_FORWARD) { + } else if (initialDialogsType == DIALOGS_TYPE_FORWARD || clickSelectsDialog()) { if (commentView != null) { commentView.onDestroy(); } @@ -5098,77 +5095,19 @@ public void onMiniListClicked() { } if (searchString == null && initialDialogsType == DIALOGS_TYPE_DEFAULT) { - updateLayout = new FrameLayout(context) { - - private Paint paint = new Paint(); - private Matrix matrix = new Matrix(); - private LinearGradient updateGradient; - private int lastGradientWidth; - - @Override - public void draw(Canvas canvas) { - if (updateGradient != null) { - paint.setColor(0xffffffff); - paint.setShader(updateGradient); - updateGradient.setLocalMatrix(matrix); - canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint); - updateLayoutIcon.setBackgroundGradientDrawable(updateGradient); - updateLayoutIcon.draw(canvas); - } - super.draw(canvas); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - int width = MeasureSpec.getSize(widthMeasureSpec); - if (lastGradientWidth != width) { - updateGradient = new LinearGradient(0, 0, width, 0, new int[]{0xff69BF72, 0xff53B3AD}, new float[]{0.0f, 1.0f}, Shader.TileMode.CLAMP); - lastGradientWidth = width; - } - int x = (getMeasuredWidth() - updateTextView.getMeasuredWidth()) / 2; - updateLayoutIcon.setProgressRect(x, dp(13), x + dp(22), dp(13 + 22)); - } - - @Override - public void setTranslationY(float translationY) { - super.setTranslationY(translationY); - additionalFloatingTranslation2 = dp(48) - translationY; + updateButton = ApplicationLoader.applicationLoaderInstance.takeUpdateButton(context); + if (updateButton != null) { + contentView.addView(updateButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.BOTTOM)); + updateButton.onTranslationUpdate(ty -> { + additionalFloatingTranslation2 = dp(48) - ty; if (additionalFloatingTranslation2 < 0) { additionalFloatingTranslation2 = 0; } if (!floatingHidden) { updateFloatingButtonOffset(); } - } - }; - updateLayout.setWillNotDraw(false); - updateLayout.setVisibility(View.INVISIBLE); - updateLayout.setTranslationY(dp(48)); - if (Build.VERSION.SDK_INT >= 21) { - updateLayout.setBackground(Theme.getSelectorDrawable(0x40ffffff, false)); + }); } - contentView.addView(updateLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.BOTTOM)); - updateLayout.setOnClickListener(v -> { - if (!SharedConfig.isAppUpdateAvailable()) { - return; - } - AndroidUtilities.openForView(SharedConfig.pendingAppUpdate.document, true, getParentActivity()); - }); - - updateLayoutIcon = new RadialProgress2(updateLayout); - updateLayoutIcon.setColors(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff); - updateLayoutIcon.setCircleRadius(dp(11)); - updateLayoutIcon.setAsMini(); - updateLayoutIcon.setIcon(MediaActionDrawable.ICON_UPDATE, true, false); - - updateTextView = new TextView(context); - updateTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); - updateTextView.setTypeface(AndroidUtilities.bold()); - updateTextView.setText(LocaleController.getString(R.string.AppUpdateNow).toUpperCase()); - updateTextView.setTextColor(0xffffffff); - updateTextView.setPadding(dp(30), 0, 0, 0); - updateLayout.addView(updateTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 0, 0, 0, 0)); } undoViewIndex = contentView.getChildCount(); @@ -5553,9 +5492,15 @@ public boolean isStarsSubscriptionHintVisible() { } else { long starsNeeded = -c.balance; for (int i = 0; i < c.insufficientSubscriptions.size(); ++i) { - TL_stars.StarsSubscription sub = c.insufficientSubscriptions.get(i); - TLRPC.Chat chat = getMessagesController().getChat(-DialogObject.getPeerDialogId(sub.peer)); - if (chat == null) continue; + final TL_stars.StarsSubscription sub = c.insufficientSubscriptions.get(i); + final long did = DialogObject.getPeerDialogId(sub.peer); + if (did >= 0) { + TLRPC.User user = getMessagesController().getUser(did); + if (user == null) continue; + } else { + TLRPC.Chat chat = getMessagesController().getChat(-did); + if (chat == null) continue; + } starsNeeded += sub.pricing.amount; } return starsNeeded > 0; @@ -5852,11 +5797,19 @@ private void updateDialogsHint() { long starsNeeded = 0; if (c.hasInsufficientSubscriptions()) { for (int i = 0; i < c.insufficientSubscriptions.size(); ++i) { - TL_stars.StarsSubscription sub = c.insufficientSubscriptions.get(i); - TLRPC.Chat chat = getMessagesController().getChat(-DialogObject.getPeerDialogId(sub.peer)); - if (chat == null) continue; - if (s.length() > 0) s.append(", "); - s.append(chat.title); + final TL_stars.StarsSubscription sub = c.insufficientSubscriptions.get(i); + final long did = DialogObject.getPeerDialogId(sub.peer); + if (did >= 0) { + TLRPC.User user = getMessagesController().getUser(did); + if (user == null) continue; + if (s.length() > 0) s.append(", "); + s.append(UserObject.getUserName(user)); + } else { + TLRPC.Chat chat = getMessagesController().getChat(-did); + if (chat == null) continue; + if (s.length() > 0) s.append(", "); + s.append(chat.title); + } starsNeeded += sub.pricing.amount; } } @@ -5884,7 +5837,7 @@ private void updateDialogsHint() { dialogsHintCell.setCompact(true); dialogsHintCell.setOnClickListener(v -> { if (state != null && state.today.size() == 1) { - showDialog(new GiftSheet(getContext(), currentAccount, state.today.get(0).id, null, null)); + showDialog(new GiftSheet(getContext(), currentAccount, state.today.get(0).id, null, null).setBirthday()); return; } UserSelectorBottomSheet.open(0, state); @@ -5913,6 +5866,7 @@ private void updateDialogsHint() { .show(); }); updateAuthHintCellVisibility(false); + StarsController.getInstance(currentAccount).loadStarGifts(); } else if ( folderId == 0 && MessagesController.getInstance(currentAccount).pendingSuggestions.contains("BIRTHDAY_SETUP") && @@ -6317,69 +6271,6 @@ public void didFinishChatCreation(GroupCreateFinalActivity fragment, long chatId } } - private void updateAppUpdateViews(boolean animated) { - if (updateLayout == null) { - return; - } - boolean show; - if (SharedConfig.isAppUpdateAvailable()) { - String fileName = FileLoader.getAttachFileName(SharedConfig.pendingAppUpdate.document); - File path = getFileLoader().getPathToAttach(SharedConfig.pendingAppUpdate.document, true); - show = path.exists(); - } else { - show = false; - } - if (show) { - if (updateLayout.getTag() != null) { - return; - } - if (updateLayoutAnimator != null) { - updateLayoutAnimator.cancel(); - } - updateLayout.setVisibility(View.VISIBLE); - updateLayout.setTag(1); - if (animated) { - updateLayoutAnimator = new AnimatorSet(); - updateLayoutAnimator.setDuration(180); - updateLayoutAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT); - updateLayoutAnimator.playTogether(ObjectAnimator.ofFloat(updateLayout, View.TRANSLATION_Y, 0)); - updateLayoutAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - updateLayoutAnimator = null; - } - }); - updateLayoutAnimator.start(); - } else { - updateLayout.setTranslationY(0); - } - } else { - if (updateLayout.getTag() == null) { - return; - } - updateLayout.setTag(null); - if (animated) { - updateLayoutAnimator = new AnimatorSet(); - updateLayoutAnimator.setDuration(180); - updateLayoutAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT); - updateLayoutAnimator.playTogether(ObjectAnimator.ofFloat(updateLayout, View.TRANSLATION_Y, dp(48))); - updateLayoutAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (updateLayout.getTag() == null) { - updateLayout.setVisibility(View.INVISIBLE); - } - updateLayoutAnimator = null; - } - }); - updateLayoutAnimator.start(); - } else { - updateLayout.setTranslationY(dp(48)); - updateLayout.setVisibility(View.INVISIBLE); - } - } - } - private void updateContextViewPosition() { float filtersTabsHeight = 0; if (filterTabsView != null && filterTabsView.getVisibility() != View.GONE) { @@ -7962,7 +7853,7 @@ private void onItemClick(View view, int position, RecyclerListView.Adapter adapt TLRPC.TL_inputStickerSetID set = new TLRPC.TL_inputStickerSetID(); set.id = stickerSet.id; set.access_hash = stickerSet.access_hash; - showDialog(new StickersAlert(getParentActivity(), DialogsActivity.this, set, null, null)); + showDialog(new StickersAlert(getParentActivity(), DialogsActivity.this, set, null, null, false)); return; } else if (object instanceof TLRPC.TL_recentMeUrlUnknown) { return; @@ -8051,6 +7942,7 @@ private void onItemClick(View view, int position, RecyclerListView.Adapter adapt bundle.putLong("chat_id", -dialogId); bundle.putBoolean("for_select", true); bundle.putBoolean("forward_to", true); + bundle.putBoolean("bot_share_to", initialDialogsType == DIALOGS_TYPE_BOT_SHARE); bundle.putBoolean("quote", isQuote); bundle.putBoolean("reply_to", isReplyTo); TopicsFragment topicsFragment = new TopicsFragment(bundle); @@ -8336,12 +8228,25 @@ private boolean onItemLongClick(RecyclerListView listView, View view, int positi } if (onlySelect) { - if (initialDialogsType != DIALOGS_TYPE_FORWARD && initialDialogsType != DIALOGS_TYPE_WIDGET) { + if (initialDialogsType != DIALOGS_TYPE_FORWARD && !clickSelectsDialog()) { return false; } if (!validateSlowModeDialog(dialog.id)) { return false; } + if (initialDialogsType == DIALOGS_TYPE_BOT_SHARE && clickSelectsDialog() && canSelectTopics && getMessagesController().isForum(dialog.id)) { + Bundle bundle = new Bundle(); + bundle.putLong("chat_id", -dialog.id); + bundle.putBoolean("for_select", true); + bundle.putBoolean("forward_to", true); + bundle.putBoolean("bot_share_to", initialDialogsType == DIALOGS_TYPE_BOT_SHARE); + bundle.putBoolean("quote", isQuote); + bundle.putBoolean("reply_to", isReplyTo); + TopicsFragment topicsFragment = new TopicsFragment(bundle); + topicsFragment.setForwardFromDialogFragment(DialogsActivity.this); + presentFragment(topicsFragment); + return false; + } addOrRemoveSelectedDialog(dialog.id, view); updateSelectedCount(); return true; @@ -10792,7 +10697,7 @@ private void checkSuggestClearDatabase() { View databaseMigrationHint; private void updateMenuButton(boolean animated) { - if (menuDrawable == null || updateLayout == null) { + if (menuDrawable == null || updateButton == null) { return; } int type; @@ -10811,7 +10716,7 @@ private void updateMenuButton(boolean animated) { type = MenuDrawable.TYPE_DEFAULT; downloadProgress = 0.0f; } - updateAppUpdateViews(animated); + updateButton.update(animated); menuDrawable.setType(type, animated); menuDrawable.setUpdateDownloadProgress(downloadProgress, animated); } @@ -13025,6 +12930,28 @@ public long getSearchForumDialogId() { presentFragment(highlightFoundQuote(chatActivity, msg)); } }); + searchViewPager.hashtagSearchListView.setOnItemClickListener((view, position) -> { + UItem item = searchViewPager.hashtagSearchAdapter.getItem(position); + if (item.object instanceof MessageObject) { + MessageObject msg = (MessageObject) item.object; + Bundle args = new Bundle(); + if (msg.getDialogId() >= 0) { + args.putLong("user_id", msg.getDialogId()); + } else { + args.putLong("chat_id", -msg.getDialogId()); + } + args.putInt("message_id", msg.getId()); + ChatActivity chatActivity = new ChatActivity(args); + presentFragment(highlightFoundQuote(chatActivity, msg)); + } else if (item.object instanceof StoriesController.SearchStoriesList) { + StoriesController.SearchStoriesList list = (StoriesController.SearchStoriesList) item.object; + Bundle args = new Bundle(); + args.putInt("type", MediaActivity.TYPE_STORIES_SEARCH); + args.putString("hashtag", list.query); + args.putInt("storiesCount", list.getCount()); + presentFragment(new MediaActivity(args, null)); + } + }); searchViewPager.botsSearchListView.setOnItemLongClickListener((view, position) -> { Object obj = searchViewPager.botsSearchAdapter.getTopPeerObject(position); if (obj instanceof TLRPC.User) { @@ -13079,4 +13006,9 @@ public void onLongClickRelease() { searchViewPager.setFilteredSearchViewDelegate((showMediaFilters, users, dates, archive) -> DialogsActivity.this.updateFiltersView(showMediaFilters, users, dates, archive, true)); searchViewPager.setVisibility(View.GONE); } + + public boolean clickSelectsDialog() { + return initialDialogsType == DIALOGS_TYPE_WIDGET; + } + } \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/EmojiAnimationsOverlay.java b/TMessagesProj/src/main/java/org/telegram/ui/EmojiAnimationsOverlay.java index 7c6339e677f..fe1fd49f12a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/EmojiAnimationsOverlay.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/EmojiAnimationsOverlay.java @@ -1,9 +1,7 @@ package org.telegram.ui; import android.graphics.Canvas; -import android.graphics.Typeface; import android.text.TextUtils; -import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.View; import android.widget.FrameLayout; @@ -32,12 +30,10 @@ import org.telegram.messenger.Utilities; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; -import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Cells.ChatActionCell; import org.telegram.ui.Cells.ChatMessageCell; import org.telegram.ui.Components.AnimatedEmojiDrawable; import org.telegram.ui.Components.Bulletin; -import org.telegram.ui.Components.RLottieDrawable; import org.telegram.ui.Components.Reactions.AnimatedEmojiEffect; import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; import org.telegram.ui.Components.RecyclerListView; @@ -845,7 +841,7 @@ private void showStickerSetBulletin(TLRPC.TL_messages_stickerSet stickerSet, Mes Bulletin.UndoButton viewButton = new Bulletin.UndoButton(chatActivity.getParentActivity(), true, chatActivity.getResourceProvider()); layout.setButton(viewButton); viewButton.setUndoAction(() -> { - StickersAlert alert = new StickersAlert(chatActivity.getParentActivity(), chatActivity, messageObject.getInputStickerSet(), null, chatActivity.chatActivityEnterView, chatActivity.getResourceProvider()); + StickersAlert alert = new StickersAlert(chatActivity.getParentActivity(), chatActivity, messageObject.getInputStickerSet(), null, chatActivity.chatActivityEnterView, chatActivity.getResourceProvider(), false); alert.setCalcMandatoryInsets(chatActivity.isKeyboardVisible()); chatActivity.showDialog(alert); }); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/FeaturedStickersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/FeaturedStickersActivity.java index 91b87170d19..5657ec9b5eb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/FeaturedStickersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/FeaturedStickersActivity.java @@ -117,7 +117,7 @@ public boolean supportsPredictiveItemAnimations() { inputStickerSet.short_name = stickerSet.set.short_name; } inputStickerSet.access_hash = stickerSet.set.access_hash; - StickersAlert stickersAlert = new StickersAlert(getParentActivity(), FeaturedStickersActivity.this, inputStickerSet, null, null); + StickersAlert stickersAlert = new StickersAlert(getParentActivity(), FeaturedStickersActivity.this, inputStickerSet, null, null, false); stickersAlert.setInstallDelegate(new StickersAlert.StickersAlertInstallDelegate() { @Override public void onStickerSetInstalled() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/FilterCreateActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/FilterCreateActivity.java index 2bc23feffa9..e4224f31ebc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/FilterCreateActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/FilterCreateActivity.java @@ -2042,15 +2042,20 @@ public static class NewSpan extends ReplacementSpan { private boolean outline; private int color; + private int fontSize; public NewSpan(boolean outline) { + this(outline, -1); + } + public NewSpan(boolean outline, int fontSize) { this.outline = outline; + this.fontSize = fontSize; textPaint.setTypeface(AndroidUtilities.bold()); if (outline) { bgPaint.setStyle(Paint.Style.STROKE); bgPaint.setStrokeWidth(dpf2(1.33f)); - textPaint.setTextSize(dp(10)); + textPaint.setTextSize(dp(fontSize < 0 ? 10 : fontSize)); textPaint.setStyle(Paint.Style.FILL_AND_STROKE); textPaint.setStrokeWidth(dpf2(0.2f)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { @@ -2058,7 +2063,7 @@ public NewSpan(boolean outline) { } } else { bgPaint.setStyle(Paint.Style.FILL); - textPaint.setTextSize(dp(12)); + textPaint.setTextSize(dp(fontSize < 0 ? 12 : fontSize)); } } @@ -2114,7 +2119,7 @@ public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, AndroidUtilities.rectTmp.bottom += dp(1.33f); } else { r = dp(4.4f); - AndroidUtilities.rectTmp.inset(dp(-4), dp(-2.33f)); + AndroidUtilities.rectTmp.inset(dp(-4), dp(fontSize == 8 ? -3.66f : -2.33f)); } canvas.drawRoundRect(AndroidUtilities.rectTmp, r, r, bgPaint); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Gifts/GiftSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Gifts/GiftSheet.java index e6dc441f2f3..7b9a0828294 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Gifts/GiftSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Gifts/GiftSheet.java @@ -42,6 +42,7 @@ import org.telegram.messenger.AccountInstance; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.BillingController; +import org.telegram.messenger.BirthdayController; import org.telegram.messenger.BuildVars; import org.telegram.messenger.DocumentObject; import org.telegram.messenger.FileLoader; @@ -128,6 +129,8 @@ public class GiftSheet extends BottomSheetWithRecyclerListView implements Notifi private final ArrayList tabs = new ArrayList<>(); private int selectedTab; + private boolean birthday; + public GiftSheet(Context context, int currentAccount, long userId, Runnable closeParentSheet) { this(context, currentAccount, userId, null, closeParentSheet); } @@ -307,12 +310,26 @@ protected float animateByScale(View view) { adapter.update(false); updateTitle(); + if (BirthdayController.getInstance(currentAccount).isToday(userId)) { + setBirthday(); + } + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.billingProductDetailsUpdated); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.starGiftsLoaded); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.userInfoDidLoad); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.starGiftSoldOut); } + public GiftSheet setBirthday() { + return setBirthday(true); + } + + public GiftSheet setBirthday(boolean b) { + this.birthday = b; + adapter.update(false); + return this; + } + private void onGiftSuccess(boolean fromGooglePlay) { TLRPC.UserFull full = MessagesController.getInstance(currentAccount).getUserFull(dialogId); final TLObject user = MessagesController.getInstance(currentAccount).getUserOrChat(dialogId); @@ -546,7 +563,8 @@ public void fillItems(ArrayList items, UniversalAdapter adapter) { items.add(UItem.asFlicker(3, FlickerLoadingView.STAR_GIFT).setSpanCount(1)); } - final ArrayList gifts = StarsController.getInstance(currentAccount).gifts; + final StarsController s = StarsController.getInstance(currentAccount); + final ArrayList gifts = birthday ? s.birthdaySortedGifts : s.gifts; if (!MessagesController.getInstance(currentAccount).stargiftsBlocked && !gifts.isEmpty()) { items.add(UItem.asCustom(starsHeaderView)); final TreeSet prices = new TreeSet<>(); @@ -583,7 +601,7 @@ public void fillItems(ArrayList items, UniversalAdapter adapter) { items.add(GiftCell.Factory.asStarGift(selectedTab, gift)); } } - if (StarsController.getInstance(currentAccount).giftsLoading) { + if (s.giftsLoading) { items.add(UItem.asFlicker(4, FlickerLoadingView.STAR_GIFT).setSpanCount(1)); items.add(UItem.asFlicker(5, FlickerLoadingView.STAR_GIFT).setSpanCount(1)); items.add(UItem.asFlicker(6, FlickerLoadingView.STAR_GIFT).setSpanCount(1)); @@ -736,11 +754,11 @@ public void setStarsGift(TL_stars.StarGift gift) { if (gift.limited && gift.availability_remains <= 0) { ribbon.setVisibility(View.VISIBLE); - ribbon.setColor(Theme.getColor(Theme.key_text_RedBold, resourcesProvider)); + ribbon.setColor(Theme.getColor(Theme.key_gift_ribbon_soldout, resourcesProvider)); ribbon.setText(LocaleController.getString(R.string.Gift2SoldOut), true); } else if (gift.limited) { ribbon.setVisibility(View.VISIBLE); - ribbon.setColor(0xFF46A4F2); + ribbon.setColor(Theme.getColor(Theme.key_gift_ribbon, resourcesProvider)); ribbon.setText(getString(R.string.Gift2LimitedRibbon), false); } else { ribbon.setVisibility(View.GONE); @@ -788,7 +806,7 @@ public void setStarsGift(TL_stars.UserStarGift userGift) { if (userGift.gift.limited) { ribbon.setVisibility(View.VISIBLE); - ribbon.setColor(0xFF46A4F2); + ribbon.setColor(Theme.getColor(Theme.key_gift_ribbon, resourcesProvider)); ribbon.setText(LocaleController.formatString(R.string.Gift2Limited1OfRibbon, AndroidUtilities.formatWholeNumber(userGift.gift.availability_total, 0)), true); } else { ribbon.setVisibility(View.GONE); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Gifts/ProfileGiftsContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/Gifts/ProfileGiftsContainer.java index 8a88a88c3b7..a3c6edbdd23 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Gifts/ProfileGiftsContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Gifts/ProfileGiftsContainer.java @@ -77,6 +77,10 @@ private long getRandomUserId() { return 0; } + protected int processColor(int color) { + return color; + } + public ProfileGiftsContainer(Context context, int currentAccount, long userId, Theme.ResourcesProvider resourcesProvider) { super(context); @@ -218,7 +222,7 @@ public long getLastEmojisHash() { private int visibleHeight = AndroidUtilities.displaySize.y; public void setVisibleHeight(int height) { visibleHeight = height; - buttonContainer.setTranslationY(-buttonContainer.getTop() + height - dp(48 + 10 + 10 + 1f / AndroidUtilities.density)); +// buttonContainer.setTranslationY(-buttonContainer.getTop() + height - dp(48 + 10 + 10 + 1f / AndroidUtilities.density)); } public void fillItems(ArrayList items, UniversalAdapter adapter) { @@ -311,4 +315,10 @@ public static UItem asText(int color, int gravity, float textSizeDp, CharSequenc } } + public void updateColors() { + if (button != null) { + button.setBackground(Theme.createRoundRectDrawable(dp(8), processColor(Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider)))); + } + } + } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Gifts/SendGiftSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Gifts/SendGiftSheet.java index 343e63d6a9b..1262caa8868 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Gifts/SendGiftSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Gifts/SendGiftSheet.java @@ -94,6 +94,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Calendar; import java.util.Collections; import java.util.List; @@ -484,6 +485,11 @@ public Theme.ResourcesProvider getResourceProvider() { dismiss(); NotificationCenter.getInstance(UserConfig.selectedAccount).postNotificationName(NotificationCenter.giftsToUserSent); AndroidUtilities.runOnUIThread(() -> PremiumPreviewGiftSentBottomSheet.show(new ArrayList<>(Arrays.asList(user))), 250); + + MessagesController.getInstance(currentAccount).getMainSettings().edit() + .putBoolean("show_gift_for_" + dialogId, true) + .putBoolean(Calendar.getInstance().get(Calendar.YEAR) + "show_gift_for_" + dialogId, true) + .apply(); }, error -> { BoostDialogs.showToastError(getContext(), error); }); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupCallActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupCallActivity.java index ad163a32b85..0ca593f9d2c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCallActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCallActivity.java @@ -1673,7 +1673,7 @@ private void updateItems() { } else { permissionItem.setVisibility(View.GONE); } - soundItem.setVisibility(isRtmpStream() ? View.GONE : View.VISIBLE); + soundItem.setVisibility(isRtmpStream() && !call.isScheduled() ? View.GONE : View.VISIBLE); if (editTitleItem.getVisibility() == View.VISIBLE || permissionItem.getVisibility() == View.VISIBLE || inviteItem.getVisibility() == View.VISIBLE || screenItem.getVisibility() == View.VISIBLE || recordItem.getVisibility() == View.VISIBLE || leaveItem.getVisibility() == View.VISIBLE) { soundItemDivider.setVisibility(View.VISIBLE); @@ -1682,7 +1682,7 @@ private void updateItems() { } int margin = 48; - if ((VoIPService.getSharedInstance() != null && VoIPService.getSharedInstance().hasFewPeers || scheduleHasFewPeers) && !isRtmpStream()) { + if ((VoIPService.getSharedInstance() != null && VoIPService.getSharedInstance().hasFewPeers || scheduleHasFewPeers) && !isRtmpStream() && selfPeer != null) { accountSelectCell.setVisibility(View.VISIBLE); accountGap.setVisibility(View.VISIBLE); long peerId = MessageObject.getPeerId(selfPeer); @@ -4756,7 +4756,7 @@ public void showWithAction(long did, int action, Object infoObject, Object infoO screenItem = otherItem.addSubItem(screen_capture_item, R.drawable.msg_screencast, LocaleController.getString(R.string.VoipChatStartScreenCapture)); recordItem = otherItem.addSubItem(start_record_item, 0, recordCallDrawable, LocaleController.getString(R.string.VoipGroupRecordCall), true, false); recordCallDrawable.setParentView(recordItem.getImageView()); - leaveItem = otherItem.addSubItem(leave_item, R.drawable.msg_endcall, ChatObject.isChannelOrGiga(currentChat) ? LocaleController.getString(R.string.VoipChannelEndChat) : LocaleController.getString(R.string.VoipGroupEndChat)); + leaveItem = otherItem.addSubItem(leave_item, R.drawable.msg_cancel, ChatObject.isChannelOrGiga(currentChat) ? LocaleController.getString(R.string.VoipChannelEndChat) : LocaleController.getString(R.string.VoipGroupEndChat)); otherItem.setPopupItemsSelectorColor(Theme.getColor(Theme.key_voipgroup_listSelector)); otherItem.getPopupLayout().setFitItems(true); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java index 59a3ee42565..e3df80a8491 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java @@ -129,7 +129,9 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen private boolean searching; private int chatAddType; private boolean allowPremium; + private boolean allowMiniapps; private GroupCreateSpan selectedPremium; + private GroupCreateSpan selectedMiniapps; private LongSparseArray selectedContacts = new LongSparseArray<>(); private ArrayList allSpans = new ArrayList<>(); private GroupCreateSpan currentDeletingSpan; @@ -143,7 +145,7 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen private PermanentLinkBottomSheet sharedLinkBottomSheet; public interface GroupCreateActivityDelegate { - void didSelectUsers(boolean withPremium, ArrayList ids); + void didSelectUsers(boolean withPremium, boolean withMiniapps, ArrayList ids); } public interface GroupCreateActivityImportDelegate { @@ -160,10 +162,12 @@ default void needAddBot(TLRPC.User user) { private ArrayList toSelectIds; private boolean toSelectPremium; - public void select(ArrayList ids, boolean premium) { + private boolean toSelectMiniapps; + public void select(ArrayList ids, boolean premium, boolean miniapps) { if (spansContainer == null) { toSelectIds = ids; toSelectPremium = premium; + toSelectMiniapps = miniapps; return; } if (premium && selectedPremium == null) { @@ -174,6 +178,14 @@ public void select(ArrayList ids, boolean premium) { spansContainer.removeSpan(selectedPremium); selectedPremium = null; } + if (miniapps && selectedMiniapps == null) { + selectedMiniapps = new GroupCreateSpan(getContext(), "miniapps"); + spansContainer.addSpan(selectedMiniapps); + selectedMiniapps.setOnClickListener(GroupCreateActivity.this); + } else if (!miniapps && selectedMiniapps != null) { + spansContainer.removeSpan(selectedMiniapps); + selectedMiniapps = null; + } for (long id : ids) { TLObject obj; if (id < 0) { @@ -360,6 +372,9 @@ public void removeSpan(final GroupCreateSpan span) { if (span == selectedPremium) { selectedPremium = null; } + if (span == selectedMiniapps) { + selectedMiniapps = null; + } allSpans.remove(span); span.setOnClickListener(null); @@ -460,6 +475,7 @@ public GroupCreateActivity(Bundle args) { addToGroup = args.getBoolean("addToGroup", false); chatAddType = args.getInt("chatAddType", 0); allowPremium = args.getBoolean("allowPremium", false); + allowMiniapps = args.getBoolean("allowMiniapps", false); chatId = args.getLong("chatId"); channelId = args.getLong("channelId"); if (isAlwaysShare || isNeverShare || addToGroup) { @@ -768,7 +784,7 @@ public void afterTextChanged(Editable editable) { }); if (toSelectIds != null) { - select(toSelectIds, toSelectPremium); + select(toSelectIds, toSelectPremium, toSelectMiniapps); } FlickerLoadingView flickerLoadingView = new FlickerLoadingView(context); @@ -811,6 +827,18 @@ public void afterTextChanged(Editable editable) { checkVisibleRows(); return; } + if (cell.currentMiniapps) { + if (selectedMiniapps == null) { + selectedMiniapps = new GroupCreateSpan(editText.getContext(), "miniapps"); + spansContainer.addSpan(selectedMiniapps); + selectedMiniapps.setOnClickListener(GroupCreateActivity.this); + } else { + spansContainer.removeSpan(selectedMiniapps); + selectedMiniapps = null; + } + checkVisibleRows(); + return; + } Object object = cell.getObject(); long id; if (object instanceof TLRPC.User) { @@ -1088,6 +1116,10 @@ private void checkVisibleRows() { cell.setChecked(selectedPremium != null, true); cell.setCheckBoxEnabled(true); continue; + } else if (object instanceof String && "miniapps".equalsIgnoreCase((String) object)) { + cell.setChecked(selectedMiniapps != null, true); + cell.setCheckBoxEnabled(true); + continue; } else { id = 0; } @@ -1212,7 +1244,7 @@ private boolean onDonePressed(boolean alert) { } if (isAlwaysShare || isNeverShare) { if (delegate != null) { - delegate.didSelectUsers(selectedPremium != null, result); + delegate.didSelectUsers(selectedPremium != null, selectedMiniapps != null, result); } finishFragment(); } else { @@ -1328,6 +1360,7 @@ public class GroupCreateAdapter extends RecyclerListView.FastScrollAdapter { private int userTypesHeaderRow; private int firstSectionRow; private int premiumRow; + private int miniappsRow; private int usersStartRow; private int inviteViaLink; private int noContactsStubRow; @@ -1439,6 +1472,7 @@ public int getItemCount() { userTypesHeaderRow = -1; firstSectionRow = -1; premiumRow = -1; + miniappsRow = -1; if (searching) { count = searchResult.size(); int localServerCount = searchAdapterHelper.getLocalServerSearch().size(); @@ -1454,6 +1488,9 @@ public int getItemCount() { if (allowPremium) { userTypesHeaderRow = firstSectionRow = count++; premiumRow = count++; + } else if (allowMiniapps) { + userTypesHeaderRow = firstSectionRow = count++; + miniappsRow = count++; } else { firstSectionRow = count; } @@ -1607,6 +1644,10 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { cell.setPremium(); cell.setChecked(selectedPremium != null, false); return; + } else if (position == miniappsRow) { + cell.setMiniapps(); + cell.setChecked(selectedMiniapps != null, false); + return; } object = contacts.get(position - usersStartRow); } @@ -1653,7 +1694,7 @@ public int getItemViewType(int position) { if (position == userTypesHeaderRow) { return 0; } - if (position == premiumRow) { + if (position == premiumRow || position == miniappsRow) { return 1; } if (inviteViaLink != 0 && position == 0) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupStickersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupStickersActivity.java index bc5fa6e6e3e..b45c51d3930 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupStickersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupStickersActivity.java @@ -262,7 +262,7 @@ private void onStickerSetClicked(boolean isSelected, TLRPC.TL_messages_stickerSe inputStickerSetShortName.short_name = stickerSet.set.short_name; inputStickerSet = inputStickerSetShortName; } - StickersAlert stickersAlert = new StickersAlert(getParentActivity(), GroupStickersActivity.this, inputStickerSet, !remote ? stickerSet : null, null); + StickersAlert stickersAlert = new StickersAlert(getParentActivity(), GroupStickersActivity.this, inputStickerSet, !remote ? stickerSet : null, null, false); stickersAlert.setCustomButtonDelegate(new StickersAlert.StickersAlertCustomButtonDelegate() { @Override public int getCustomButtonTextColorKey() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/IUpdateButton.java b/TMessagesProj/src/main/java/org/telegram/ui/IUpdateButton.java new file mode 100644 index 00000000000..f08c161b997 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/IUpdateButton.java @@ -0,0 +1,22 @@ +package org.telegram.ui; + +import android.app.Activity; +import android.content.Context; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import androidx.annotation.Keep; + +import org.telegram.messenger.Utilities; + +@Keep +public abstract class IUpdateButton extends FrameLayout { + @Keep + public IUpdateButton(Context context) { + super(context); + } + @Keep + public void onTranslationUpdate(Utilities.Callback onTranslationUpdate) {} + @Keep + public void update(boolean animated) {} +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java index 0b35d2b124f..c427ed8b0e1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java @@ -216,7 +216,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import java.util.regex.Matcher; @@ -226,6 +225,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigationLayout.INavigationLayoutDelegate, NotificationCenter.NotificationCenterDelegate, DialogsActivity.DialogsActivityDelegate { public final static String EXTRA_FORCE_NOT_INTERNAL_APPS = "force_not_internal_apps"; + public final static String EXTRA_FORCE_REQUEST = "force_request"; public final static Pattern PREFIX_T_ME_PATTERN = Pattern.compile("^(?:http(?:s|)://|)([A-z0-9-]+?)\\.t\\.me"); public static boolean isActive; @@ -983,29 +983,29 @@ private void showAttachMenuBot(TLRPC.TL_attachMenuBot attachMenuBot, String star drawerLayoutContainer.closeDrawer(); BaseFragment lastFragment = getLastFragment(); if (lastFragment == null) return; - WebViewRequestProps props = WebViewRequestProps.of(currentAccount, attachMenuBot.bot_id, attachMenuBot.bot_id, attachMenuBot.short_name, null, BotWebViewAttachedSheet.TYPE_SIMPLE_WEB_VIEW_BUTTON, 0, false, null, false, startApp, null, BotWebViewSheet.FLAG_FROM_SIDE_MENU, false); + WebViewRequestProps props = WebViewRequestProps.of(currentAccount, attachMenuBot.bot_id, attachMenuBot.bot_id, attachMenuBot.short_name, null, BotWebViewAttachedSheet.TYPE_SIMPLE_WEB_VIEW_BUTTON, 0, false, null, false, startApp, null, BotWebViewSheet.FLAG_FROM_SIDE_MENU, false, false); if (getBottomSheetTabs() != null && getBottomSheetTabs().tryReopenTab(props) != null) { return; } - if (AndroidUtilities.isTablet()) { +// if (AndroidUtilities.isTablet() || true) { BotWebViewSheet webViewSheet = new BotWebViewSheet(this, lastFragment.getResourceProvider()); webViewSheet.setNeedsContext(false); webViewSheet.setDefaultFullsize(sidemenu); webViewSheet.setParentActivity(this); webViewSheet.requestWebView(lastFragment, props); webViewSheet.show(); - } else { - BaseFragment fragment = lastFragment; - if (fragment.getParentLayout() instanceof ActionBarLayout) { - fragment = ((ActionBarLayout) fragment.getParentLayout()).getSheetFragment(); - } - BotWebViewAttachedSheet webViewSheet = fragment.createBotViewer(); - webViewSheet.setNeedsContext(false); - webViewSheet.setDefaultFullsize(sidemenu); - webViewSheet.setParentActivity(this); - webViewSheet.requestWebView(lastFragment, props); - webViewSheet.show(); - } +// } else { +// BaseFragment fragment = lastFragment; +// if (fragment.getParentLayout() instanceof ActionBarLayout) { +// fragment = ((ActionBarLayout) fragment.getParentLayout()).getSheetFragment(); +// } +// BotWebViewAttachedSheet webViewSheet = fragment.createBotViewer(); +// webViewSheet.setNeedsContext(false); +// webViewSheet.setDefaultFullsize(sidemenu); +// webViewSheet.setParentActivity(this); +// webViewSheet.requestWebView(lastFragment, props); +// webViewSheet.show(); +// } } @Override @@ -1161,7 +1161,7 @@ protected boolean isActionBarVisible() { }); - layersActionBarLayout = new ActionBarLayout(this, true); + layersActionBarLayout = new ActionBarLayout(this, false); layersActionBarLayout.setRemoveActionBarExtraHeight(true); layersActionBarLayout.setBackgroundView(shadowTablet); layersActionBarLayout.setUseAlphaAnimations(true); @@ -1380,6 +1380,9 @@ public void checkSystemBarColors(boolean useCurrentFragment, boolean checkStatus } } } + for (BotWebViewSheet sheet : BotWebViewSheet.activeSheets) { + color = sheet.getNavigationBarColor(color); + } setNavigationBarColor(color, checkButtons); setLightNavigationBar(AndroidUtilities.computePerceivedBrightness(color) >= .721f); } @@ -1812,6 +1815,7 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool String newContactName = null; String newContactPhone = null; boolean forceNotInternalForApps = intent.getBooleanExtra(EXTRA_FORCE_NOT_INTERNAL_APPS, false); + boolean forceRequest = intent.getBooleanExtra(EXTRA_FORCE_REQUEST, false); photoPathsArray = null; videoPath = null; @@ -2144,6 +2148,7 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool String attachMenuBotToOpen = null; String attachMenuBotChoose = null; boolean botCompact = false; + boolean botFullscreen = false; boolean openProfile = false; int storyId = 0; final String scheme = data.getScheme(); @@ -2388,6 +2393,7 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool attachMenuBotChoose = data.getQueryParameter("choose"); attachMenuBotToOpen = data.getQueryParameter("attach"); botCompact = TextUtils.equals(data.getQueryParameter("mode"), "compact"); + botFullscreen = TextUtils.equals(data.getQueryParameter("mode"), "fullscreen"); openProfile = data.getBooleanQueryParameter("profile", false); threadId = Utilities.parseLong(data.getQueryParameter("thread")); text = data.getQueryParameter("text"); @@ -2854,7 +2860,7 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool if (message != null && message.startsWith("@")) { message = " " + message; } - runLinkRequest(intentAccount[0], username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, login, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, 0, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, startApp, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, botCompact, openedTelegram, openProfile); + runLinkRequest(intentAccount[0], username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, login, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, 0, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, startApp, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, botCompact, botFullscreen, openedTelegram, openProfile, forceRequest); } else { try (Cursor cursor = getContentResolver().query(intent.getData(), null, null, null, null)) { if (cursor != null) { @@ -3875,9 +3881,9 @@ private void runLinkRequest(final int intentAccount, final boolean forceNotInternalForApps, final int storyId, final boolean isBoost, - final String chatLinkSlug, boolean botCompact, boolean openedTelegram, boolean openProfile) { + final String chatLinkSlug, boolean botCompact, boolean botFullscreen, boolean openedTelegram, boolean openProfile, boolean forceRequest) { if (state == 0 && ChatActivity.SCROLL_DEBUG_DELAY && progress != null) { - Runnable runnable = () -> runLinkRequest(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, 1, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, botCompact, openedTelegram, openProfile); + Runnable runnable = () -> runLinkRequest(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, 1, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, botCompact, botFullscreen, openedTelegram, openProfile, forceRequest); progress.init(); progress.onCancel(() -> AndroidUtilities.cancelRunOnUIThread(runnable)); AndroidUtilities.runOnUIThread(runnable, 7500); @@ -3887,7 +3893,7 @@ private void runLinkRequest(final int intentAccount, if (account != intentAccount) { switchToAccount(account, true); } - runLinkRequest(account, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, 1, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, botCompact, openedTelegram, openProfile); + runLinkRequest(account, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, 1, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, botCompact, botFullscreen, openedTelegram, openProfile, forceRequest); }).show(); return; } else if (code != null) { @@ -3896,7 +3902,7 @@ private void runLinkRequest(final int intentAccount, } else { AlertDialog.Builder builder = new AlertDialog.Builder(LaunchActivity.this); builder.setTitle(LocaleController.getString(R.string.AppName)); - builder.setMessage(AndroidUtilities.replaceTags(LocaleController.formatString("OtherLoginCode", R.string.OtherLoginCode, code))); + builder.setMessage(AndroidUtilities.replaceTags(LocaleController.formatString(R.string.OtherLoginCode, code))); builder.setPositiveButton(LocaleController.getString(R.string.OK), null); showAlertDialog(builder); } @@ -3987,7 +3993,11 @@ private void runLinkRequest(final int intentAccount, req.invoice = invoiceSlug; requestId[0] = ConnectionsManager.getInstance(intentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { if (error != null) { - BulletinFactory.of(mainFragmentsStack.get(mainFragmentsStack.size() - 1)).createErrorBulletin(LocaleController.getString(R.string.PaymentInvoiceLinkInvalid)).show(); + if ("SUBSCRIPTION_ALREADY_ACTIVE".equalsIgnoreCase(error.text)) { + BulletinFactory.of(mainFragmentsStack.get(mainFragmentsStack.size() - 1)).createErrorBulletin(LocaleController.getString(R.string.PaymentInvoiceSubscriptionLinkAlreadyPaid)).show(); + } else { + BulletinFactory.of(mainFragmentsStack.get(mainFragmentsStack.size() - 1)).createErrorBulletin(LocaleController.getString(R.string.PaymentInvoiceLinkInvalid)).show(); + } } else if (!LaunchActivity.this.isFinishing()) { PaymentFormActivity paymentFormActivity = null; if (response instanceof TLRPC.TL_payments_paymentFormStars) { @@ -4078,7 +4088,7 @@ private void runLinkRequest(final int intentAccount, getAttachMenuBot.bot = MessagesController.getInstance(intentAccount).getInputUser(peerId); ConnectionsManager.getInstance(intentAccount).sendRequest(getAttachMenuBot, (response1, error1) -> AndroidUtilities.runOnUIThread(() -> { if (error1 != null) { - AndroidUtilities.runOnUIThread(() -> runLinkRequest(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, null, null, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, botCompact, openedTelegram, openProfile)); + AndroidUtilities.runOnUIThread(() -> runLinkRequest(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, null, null, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, botCompact, botFullscreen, openedTelegram, openProfile, forceRequest)); } else if (response1 instanceof TLRPC.TL_attachMenuBotsBot) { TLRPC.TL_attachMenuBotsBot bot = (TLRPC.TL_attachMenuBotsBot) response1; TLRPC.TL_attachMenuBot attachBot = bot.bot; @@ -4099,7 +4109,7 @@ private void runLinkRequest(final int intentAccount, } }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); - processWebAppBot(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, user, dismissLoading, botAttachable, true, botCompact, openedTelegram, openProfile); + processWebAppBot(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, user, dismissLoading, botAttachable, true, botCompact, botFullscreen, openedTelegram, openProfile, forceRequest); }, null, progress != null ? progress::end : null); } else if (attachBot.request_write_access || forceNotInternalForApps) { AtomicBoolean allowWrite = new AtomicBoolean(true); @@ -4119,15 +4129,15 @@ private void runLinkRequest(final int intentAccount, } }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); - processWebAppBot(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, user, dismissLoading, false, false, botCompact, openedTelegram, openProfile); + processWebAppBot(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, user, dismissLoading, false, false, botCompact, botFullscreen, openedTelegram, openProfile, forceRequest); }); } else { - processWebAppBot(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, user, dismissLoading, false, false, botCompact, openedTelegram, openProfile); + processWebAppBot(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, user, dismissLoading, false, false, botCompact, botFullscreen, openedTelegram, openProfile, forceRequest); } } })); } else { - processWebAppBot(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, user, dismissLoading, false, false, botCompact, openedTelegram, openProfile); + processWebAppBot(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, user, dismissLoading, false, false, botCompact, botFullscreen, openedTelegram, openProfile, forceRequest); } return; } @@ -4144,7 +4154,7 @@ private void runLinkRequest(final int intentAccount, if (botAppStartParam != null) { TLRPC.User user = MessagesController.getInstance(intentAccount).getUser(peerId); if (user != null && user.bot) { - MessagesController.getInstance(intentAccount).openApp(null, user, botAppStartParam, 0, progress); + MessagesController.getInstance(intentAccount).openApp(null, user, botAppStartParam, 0, progress, botCompact, botFullscreen); } } else if (setAsAttachBot != null && attachMenuBotToOpen == null) { TLRPC.User user = MessagesController.getInstance(intentAccount).getUser(peerId); @@ -4740,10 +4750,10 @@ public void onError() { StickersAlert alert; if (fragment instanceof ChatActivity) { ChatActivity chatActivity = (ChatActivity) fragment; - alert = new StickersAlert(LaunchActivity.this, fragment, stickerset, null, chatActivity.getChatActivityEnterViewForStickers(), chatActivity.getResourceProvider()); + alert = new StickersAlert(LaunchActivity.this, fragment, stickerset, null, chatActivity.getChatActivityEnterViewForStickers(), chatActivity.getResourceProvider(), false); alert.setCalcMandatoryInsets(chatActivity.isKeyboardVisible()); } else { - alert = new StickersAlert(LaunchActivity.this, fragment, stickerset, null, null); + alert = new StickersAlert(LaunchActivity.this, fragment, stickerset, null, null, false); } alert.probablyEmojis = emoji != null; fragment.showDialog(alert); @@ -5188,7 +5198,7 @@ private void processWebAppBot(final int intentAccount, final boolean isBoost, final String chatLinkSlug, TLRPC.User user, - Runnable dismissLoading, boolean botAttachable, boolean ignoreInactive, boolean botCompact, boolean openedTelegram, boolean openProfile) { + Runnable dismissLoading, boolean botAttachable, boolean ignoreInactive, boolean botCompact, boolean botFullscreen, boolean openedTelegram, boolean openProfile, boolean forceRequest) { TLRPC.TL_messages_getBotApp getBotApp = new TLRPC.TL_messages_getBotApp(); TLRPC.TL_inputBotAppShortName app = new TLRPC.TL_inputBotAppShortName(); @@ -5200,7 +5210,7 @@ private void processWebAppBot(final int intentAccount, progress.end(); } if (error1 != null) { - AndroidUtilities.runOnUIThread(() -> runLinkRequest(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, null, null, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, botCompact, openedTelegram, openProfile)); + AndroidUtilities.runOnUIThread(() -> runLinkRequest(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, null, null, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, botCompact, botFullscreen, openedTelegram, openProfile, forceRequest)); } else { TLRPC.TL_messages_botApp botApp = (TLRPC.TL_messages_botApp) response1; AndroidUtilities.runOnUIThread(() -> { @@ -5210,38 +5220,41 @@ private void processWebAppBot(final int intentAccount, BaseFragment lastFragment = mainFragmentsStack == null || mainFragmentsStack.isEmpty() ? null : mainFragmentsStack.get(mainFragmentsStack.size() - 1); Runnable loadBotSheet = () -> { if (lastFragment == null || !isActive || isFinishing() || isDestroyed()) return; - WebViewRequestProps props = WebViewRequestProps.of(intentAccount, user.id, user.id, null, null, BotWebViewAttachedSheet.TYPE_WEB_VIEW_BOT_APP, 0, false, botApp.app, allowWrite.get(), botAppStartParam, user, 0, botCompact); + WebViewRequestProps props = WebViewRequestProps.of(intentAccount, user.id, user.id, null, null, BotWebViewAttachedSheet.TYPE_WEB_VIEW_BOT_APP, 0, false, botApp.app, allowWrite.get(), botAppStartParam, user, 0, botCompact, botFullscreen); if (getBottomSheetTabs() != null && getBottomSheetTabs().tryReopenTab(props) != null) { return; } SharedPrefsHelper.setWebViewConfirmShown(currentAccount, user.id, true); - if (AndroidUtilities.isTablet()) { +// if (AndroidUtilities.isTablet() || true) { BotWebViewSheet sheet = new BotWebViewSheet(LaunchActivity.this, lastFragment != null ? lastFragment.getResourceProvider() : null); sheet.setWasOpenedByLinkIntent(openedTelegram); sheet.setDefaultFullsize(!botCompact); - sheet.setNeedsContext(false); - sheet.setParentActivity(LaunchActivity.this); - sheet.requestWebView(lastFragment, props); - sheet.show(); - if (botApp.inactive || forceNotInternalForApps) { - sheet.showJustAddedBulletin(); - } - } else { - BaseFragment fragment = lastFragment; - if (fragment.getParentLayout() instanceof ActionBarLayout) { - fragment = ((ActionBarLayout) fragment.getParentLayout()).getSheetFragment(); + if (botFullscreen) { + sheet.setFullscreen(true, false); } - BotWebViewAttachedSheet sheet = fragment.createBotViewer(); - sheet.setWasOpenedByLinkIntent(openedTelegram); - sheet.setDefaultFullsize(!botCompact); sheet.setNeedsContext(false); sheet.setParentActivity(LaunchActivity.this); - sheet.requestWebView(fragment, props); + sheet.requestWebView(lastFragment, props); sheet.show(); if (botApp.inactive || forceNotInternalForApps) { sheet.showJustAddedBulletin(); } - } +// } else { +// BaseFragment fragment = lastFragment; +// if (fragment.getParentLayout() instanceof ActionBarLayout) { +// fragment = ((ActionBarLayout) fragment.getParentLayout()).getSheetFragment(); +// } +// BotWebViewAttachedSheet sheet = fragment.createBotViewer(); +// sheet.setWasOpenedByLinkIntent(openedTelegram); +// sheet.setDefaultFullsize(!botCompact); +// sheet.setNeedsContext(false); +// sheet.setParentActivity(LaunchActivity.this); +// sheet.requestWebView(fragment, props); +// sheet.show(); +// if (botApp.inactive || forceNotInternalForApps) { +// sheet.showJustAddedBulletin(); +// } +// } }; if (ignoreInactive) { @@ -5270,36 +5283,37 @@ private void processAttachedMenuBotFromShortcut(long botId) { } } } - AtomicBoolean isMenuBotsUpdated = new AtomicBoolean(MediaDataController.getInstance(currentAccount).isMenuBotsUpdatedLocal()); - if (!isMenuBotsUpdated.get()) { - final CountDownLatch countDownLatch = new CountDownLatch(1); - MessagesStorage.getInstance(currentAccount).getStorageQueue().postRunnable(() -> { - isMenuBotsUpdated.set(MediaDataController.getInstance(currentAccount).isMenuBotsUpdatedLocal()); - countDownLatch.countDown(); - }); - try { - countDownLatch.await(); - } catch (Exception e) { - FileLog.e(e); - return; + BaseFragment fragment = getSafeLastFragment(); + if (fragment != null && fragment.sheetsStack != null) { + for (int i = 0; i < fragment.sheetsStack.size(); ++i) { + if (fragment.sheetsStack.get(i).isShown() && fragment.sheetsStack.get(i) instanceof BotWebViewAttachedSheet && ((BotWebViewAttachedSheet) fragment.sheetsStack.get(i)).getBotId() == botId) { + return; + } } } - if (isMenuBotsUpdated.get()) { - TLRPC.TL_attachMenuBots menuBots = MediaDataController.getInstance(currentAccount).getAttachMenuBots(); - if (menuBots.bots.isEmpty()) { - // Bot is removed from the menu. - MediaDataController.getInstance(currentAccount).uninstallShortcut(botId, MediaDataController.SHORTCUT_TYPE_ATTACHED_BOT); - return; - } - for (int i = 0; i < menuBots.bots.size(); i++) { - if (menuBots.bots.get(i).bot_id == botId) { - if (getLastFragment() != null) { - showAttachMenuBot(menuBots.bots.get(i), null, false); - } + fragment = actionBarLayout.getSheetFragment(false); + if (fragment != null && fragment.sheetsStack != null) { + for (int i = 0; i < fragment.sheetsStack.size(); ++i) { + if (fragment.sheetsStack.get(i).isShown() && fragment.sheetsStack.get(i) instanceof BotWebViewAttachedSheet && ((BotWebViewAttachedSheet) fragment.sheetsStack.get(i)).getBotId() == botId) { return; } } } + Utilities.Callback open = user -> { + MessagesController.getInstance(currentAccount).openApp(user, 0); + }; + TLRPC.User bot = MessagesController.getInstance(currentAccount).getUser(botId); + if (bot != null) { + open.run(bot); + return; + } + MessagesStorage.getInstance(currentAccount).getStorageQueue().postRunnable(() -> { + TLRPC.User user = MessagesStorage.getInstance(currentAccount).getUser(botId); + AndroidUtilities.runOnUIThread(() -> { + MessagesController.getInstance(currentAccount).putUser(user, true); + open.run(user); + }); + }); } private void processBoostDialog(Long peerId, Runnable dismissLoading, Browser.Progress progress) { @@ -8493,6 +8507,12 @@ public static void dismissAllWeb() { sheet.dismiss(true); } } + + final ArrayList botSheets = new ArrayList<>(); + for (BotWebViewSheet sheet : BotWebViewSheet.activeSheets) + botSheets.add(sheet); + for (BotWebViewSheet sheet : botSheets) + sheet.dismiss(true); } public static void makeRipple(float x, float y, float intensity) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java index acd3daf5e6e..bd5f5f3da07 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java @@ -208,8 +208,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No private final static int MODE_LOGIN = 0, MODE_CANCEL_ACCOUNT_DELETION = 1, MODE_CHANGE_PHONE_NUMBER = 2, - MODE_CHANGE_LOGIN_EMAIL = 3, - MODE_BALANCE_PASSWORD = 4; + MODE_CHANGE_LOGIN_EMAIL = 3; private final static int VIEW_PHONE_INPUT = 0, VIEW_CODE_MESSAGE = 1, @@ -252,8 +251,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No MODE_LOGIN, MODE_CANCEL_ACCOUNT_DELETION, MODE_CHANGE_PHONE_NUMBER, - MODE_CHANGE_LOGIN_EMAIL, - MODE_BALANCE_PASSWORD + MODE_CHANGE_LOGIN_EMAIL }) public @interface ActivityMode {} @@ -453,19 +451,6 @@ public LoginActivity cancelAccountDeletion(String phone, Bundle params, TLRPC.TL return this; } - private TLRPC.InputChannel channel; - private TLRPC.TL_account_password currentPassword; - private Utilities.Callback2 passwordFinishCallback; - - public LoginActivity promptPassword(TLRPC.TL_account_password currentPassword, TLRPC.InputChannel channel, Utilities.Callback2 callback) { - activityMode = MODE_BALANCE_PASSWORD; - currentViewNum = VIEW_PASSWORD; - this.channel = channel; - this.currentPassword = currentPassword; - passwordFinishCallback = callback; - return this; - } - public LoginActivity changePhoneNumber() { activityMode = MODE_CHANGE_PHONE_NUMBER; return this; @@ -1044,7 +1029,7 @@ protected void onDialogDismiss(Dialog dialog) { @Override public boolean onBackPressed() { - if (currentViewNum == VIEW_PHONE_INPUT || activityMode == MODE_CHANGE_LOGIN_EMAIL && currentViewNum == VIEW_ADD_EMAIL || activityMode == MODE_BALANCE_PASSWORD && currentViewNum == VIEW_PASSWORD) { + if (currentViewNum == VIEW_PHONE_INPUT || activityMode == MODE_CHANGE_LOGIN_EMAIL && currentViewNum == VIEW_ADD_EMAIL) { for (int a = 0; a < views.length; a++) { if (views[a] != null) { views[a].onDestroyActivity(); @@ -3163,7 +3148,7 @@ private void onConfirm(PhoneNumberConfirmView confirmView) { } else if (error.text.contains("PHONE_NUMBER_FLOOD")) { needShowAlert(getString(R.string.RestorePasswordNoEmailTitle), getString("PhoneNumberFlood", R.string.PhoneNumberFlood)); } else if (error.text.contains("PHONE_NUMBER_BANNED")) { - needShowInvalidAlert(LoginActivity.this, phone, phoneInputData, true); + needShowInvalidAlert(LoginActivity.this, phone, phoneInputData, true); } else if (error.text.contains("PHONE_CODE_EMPTY") || error.text.contains("PHONE_CODE_INVALID")) { needShowAlert(getString(R.string.RestorePasswordNoEmailTitle), getString("InvalidCode", R.string.InvalidCode)); } else if (error.text.contains("PHONE_CODE_EXPIRED")) { @@ -5125,83 +5110,73 @@ public LoginActivityPasswordView(Context context) { addView(bottomContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.BOTTOM)); VerticalPositionAutoAnimator.attach(cancelButton); - if (activityMode == MODE_BALANCE_PASSWORD) { - cancelButton.setVisibility(View.GONE); - currentPassword = LoginActivity.this.currentPassword; - if (currentPassword != null && !TextUtils.isEmpty(currentPassword.hint)) { - codeField.setHint(currentPassword.hint); - } else { - codeField.setHint(null); + cancelButton.setOnClickListener(view -> { + if (radialProgressView.getTag() != null) { + return; } - } else { - cancelButton.setOnClickListener(view -> { - if (radialProgressView.getTag() != null) { - return; - } - if (currentPassword.has_recovery) { - needShowProgress(0); - TLRPC.TL_auth_requestPasswordRecovery req = new TLRPC.TL_auth_requestPasswordRecovery(); - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { - needHideProgress(false); - if (error == null) { - final TLRPC.TL_auth_passwordRecovery res = (TLRPC.TL_auth_passwordRecovery) response; - if (getParentActivity() == null) { - return; - } - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - - String rawPattern = res.email_pattern; - SpannableStringBuilder emailPattern = SpannableStringBuilder.valueOf(rawPattern); - int startIndex = rawPattern.indexOf('*'), endIndex = rawPattern.lastIndexOf('*'); - if (startIndex != endIndex && startIndex != -1 && endIndex != -1) { - TextStyleSpan.TextStyleRun run = new TextStyleSpan.TextStyleRun(); - run.flags |= TextStyleSpan.FLAG_STYLE_SPOILER; - run.start = startIndex; - run.end = endIndex + 1; - emailPattern.setSpan(new TextStyleSpan(run), startIndex, endIndex + 1, 0); - } - builder.setMessage(AndroidUtilities.formatSpannable(getString(R.string.RestoreEmailSent), emailPattern)); - builder.setTitle(getString("RestoreEmailSentTitle", R.string.RestoreEmailSentTitle)); - builder.setPositiveButton(getString(R.string.Continue), (dialogInterface, i) -> { - Bundle bundle = new Bundle(); - bundle.putString("email_unconfirmed_pattern", res.email_pattern); - bundle.putString("password", passwordString); - bundle.putString("requestPhone", requestPhone); - bundle.putString("phoneHash", phoneHash); - bundle.putString("phoneCode", phoneCode); - setPage(VIEW_RECOVER, true, bundle, false); - }); - Dialog dialog = showDialog(builder.create()); - if (dialog != null) { - dialog.setCanceledOnTouchOutside(false); - dialog.setCancelable(false); - } - } else { - if (error.text.startsWith("FLOOD_WAIT")) { - int time = Utilities.parseInt(error.text); - String timeString; - if (time < 60) { - timeString = LocaleController.formatPluralString("Seconds", time); - } else { - timeString = LocaleController.formatPluralString("Minutes", time / 60); - } - needShowAlert(getString(R.string.WrongCodeTitle), LocaleController.formatString("FloodWaitTime", R.string.FloodWaitTime, timeString)); + if (currentPassword.has_recovery) { + needShowProgress(0); + TLRPC.TL_auth_requestPasswordRecovery req = new TLRPC.TL_auth_requestPasswordRecovery(); + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + needHideProgress(false); + if (error == null) { + final TLRPC.TL_auth_passwordRecovery res = (TLRPC.TL_auth_passwordRecovery) response; + if (getParentActivity() == null) { + return; + } + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + + String rawPattern = res.email_pattern; + SpannableStringBuilder emailPattern = SpannableStringBuilder.valueOf(rawPattern); + int startIndex = rawPattern.indexOf('*'), endIndex = rawPattern.lastIndexOf('*'); + if (startIndex != endIndex && startIndex != -1 && endIndex != -1) { + TextStyleSpan.TextStyleRun run = new TextStyleSpan.TextStyleRun(); + run.flags |= TextStyleSpan.FLAG_STYLE_SPOILER; + run.start = startIndex; + run.end = endIndex + 1; + emailPattern.setSpan(new TextStyleSpan(run), startIndex, endIndex + 1, 0); + } + builder.setMessage(AndroidUtilities.formatSpannable(getString(R.string.RestoreEmailSent), emailPattern)); + builder.setTitle(getString("RestoreEmailSentTitle", R.string.RestoreEmailSentTitle)); + builder.setPositiveButton(getString(R.string.Continue), (dialogInterface, i) -> { + Bundle bundle = new Bundle(); + bundle.putString("email_unconfirmed_pattern", res.email_pattern); + bundle.putString("password", passwordString); + bundle.putString("requestPhone", requestPhone); + bundle.putString("phoneHash", phoneHash); + bundle.putString("phoneCode", phoneCode); + setPage(VIEW_RECOVER, true, bundle, false); + }); + Dialog dialog = showDialog(builder.create()); + if (dialog != null) { + dialog.setCanceledOnTouchOutside(false); + dialog.setCancelable(false); + } + } else { + if (error.text.startsWith("FLOOD_WAIT")) { + int time = Utilities.parseInt(error.text); + String timeString; + if (time < 60) { + timeString = LocaleController.formatPluralString("Seconds", time); } else { - needShowAlert(getString(R.string.RestorePasswordNoEmailTitle), error.text); + timeString = LocaleController.formatPluralString("Minutes", time / 60); } + needShowAlert(getString(R.string.WrongCodeTitle), LocaleController.formatString("FloodWaitTime", R.string.FloodWaitTime, timeString)); + } else { + needShowAlert(getString(R.string.RestorePasswordNoEmailTitle), error.text); } - }), ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagWithoutLogin); - } else { - AndroidUtilities.hideKeyboard(codeField); - new AlertDialog.Builder(context) - .setTitle(getString(R.string.RestorePasswordNoEmailTitle)) - .setMessage(getString(R.string.RestorePasswordNoEmailText)) - .setPositiveButton(getString(R.string.Close), null) - .setNegativeButton(getString(R.string.ResetAccount), (dialog, which) -> tryResetAccount(requestPhone, phoneHash, phoneCode)) - .show(); - } - }); - } + } + }), ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagWithoutLogin); + } else { + AndroidUtilities.hideKeyboard(codeField); + new AlertDialog.Builder(context) + .setTitle(getString(R.string.RestorePasswordNoEmailTitle)) + .setMessage(getString(R.string.RestorePasswordNoEmailText)) + .setPositiveButton(getString(R.string.Close), null) + .setNegativeButton(getString(R.string.ResetAccount), (dialog, which) -> tryResetAccount(requestPhone, phoneHash, phoneCode)) + .show(); + } + }); } @Override @@ -5307,10 +5282,7 @@ public void onNextPressed(String code) { return; } - if (response instanceof TL_stats.TL_broadcastRevenueWithdrawalUrl) { - passwordFinishCallback.run((TL_stats.TL_broadcastRevenueWithdrawalUrl) response, null); - finishFragment(); - } else if (response instanceof TLRPC.TL_auth_authorization) { + if (response instanceof TLRPC.TL_auth_authorization) { showDoneButton(false, true); postDelayed(() -> { needHideProgress(false, false); @@ -5344,16 +5316,9 @@ public void onNextPressed(String code) { requestDelegate.run(null, error); return; } - if (activityMode == MODE_BALANCE_PASSWORD) { - final TL_stats.TL_getBroadcastRevenueWithdrawalUrl req = new TL_stats.TL_getBroadcastRevenueWithdrawalUrl(); - req.channel = channel; - req.password = password; - ConnectionsManager.getInstance(currentAccount).sendRequest(req, requestDelegate, ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagWithoutLogin); - } else { - final TLRPC.TL_auth_checkPassword req = new TLRPC.TL_auth_checkPassword(); - req.password = password; - ConnectionsManager.getInstance(currentAccount).sendRequest(req, requestDelegate, ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagWithoutLogin); - } + final TLRPC.TL_auth_checkPassword req = new TLRPC.TL_auth_checkPassword(); + req.password = password; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, requestDelegate, ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagWithoutLogin); } }); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PaymentFormActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PaymentFormActivity.java index 6d2af53f08b..63c6eb76d48 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PaymentFormActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PaymentFormActivity.java @@ -66,6 +66,7 @@ import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.dynamicanimation.animation.FloatValueHolder; import androidx.dynamicanimation.animation.SpringAnimation; @@ -359,6 +360,7 @@ default void currentPasswordUpdated(TLRPC.account_Password password) { } private class TelegramWebviewProxy { + @Keep @JavascriptInterface public void postEvent(final String eventName, final String eventData) { AndroidUtilities.runOnUIThread(() -> { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java index 98e17c787d2..ff60591b70f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java @@ -71,6 +71,7 @@ import android.text.TextPaint; import android.text.TextUtils; import android.text.style.ClickableSpan; +import android.text.style.ForegroundColorSpan; import android.text.style.LineHeightSpan; import android.text.style.URLSpan; import android.text.util.Linkify; @@ -184,6 +185,8 @@ import org.telegram.messenger.WebFile; import org.telegram.messenger.browser.Browser; import org.telegram.messenger.camera.Size; +import org.telegram.messenger.video.OldVideoPlayerRewinder; +import org.telegram.messenger.video.VideoFramesRewinder; import org.telegram.messenger.video.VideoPlayerRewinder; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLObject; @@ -191,6 +194,7 @@ import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; +import org.telegram.ui.ActionBar.ActionBarMenuSlider; import org.telegram.ui.ActionBar.ActionBarMenuSubItem; import org.telegram.ui.ActionBar.ActionBarPopupWindow; import org.telegram.ui.ActionBar.AdjustPanLayoutHelper; @@ -256,6 +260,7 @@ import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.Components.ScaleStateListAnimator; +import org.telegram.ui.Components.SeekSpeedDrawable; import org.telegram.ui.Components.ShareAlert; import org.telegram.ui.Components.SizeNotifierFrameLayoutPhoto; import org.telegram.ui.Components.SpeedIconDrawable; @@ -806,20 +811,23 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { private TextView docInfoTextView; private TextView doneButtonFullWidth; private ActionBarMenuItem menuItem; - private OptionsSpeedIconDrawable menuItemIcon; + private ActionBarMenuItem videoItem; private ActionBarMenuSubItem allMediaItem; - private ActionBarMenuSubItem speedItem; - private ActionBarMenuSubItem qualityItem; + private ActionBarMenuSlider.SpeedSlider speedItem; + private ActionBarMenuSubItem loopItem; + private ActionBarMenuSubItem galleryButton; + private ActionBarPopupWindow.GapView galleryGap; + private ActionBarMenuSubItem pipItem; + private ChooseQualityLayout.QualityIcon videoItemIcon; + private LinearLayout videoQualityLayout; + private final ArrayList videoQualityItems = new ArrayList<>(); private ActionBarPopupWindow.GapView speedGap; private ActionBarMenu menu; private ActionBarMenuItem sendItem; private ActionBarMenuItem editItem; - private ActionBarMenuItem pipItem; private ActionBarMenuItem masksItem; private LinearLayout itemsLayout; - private ChooseQualityLayout.QualityIcon qualityIcon; - private ChooseSpeedLayout chooseSpeedLayout; - private ChooseQualityLayout chooseQualityLayout; + private SpeedButtonsLayout chooseSpeedLayout; private ChooseDownloadQualityLayout chooseDownloadQualityLayout; private Map actionBarItemsVisibility = new HashMap<>(3); private BackgroundDrawable backgroundDrawable = new BackgroundDrawable(0xff000000); @@ -860,6 +868,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { private int touchSlop; private VideoForwardDrawable videoForwardDrawable; + private SeekSpeedDrawable seekSpeedDrawable; private AnimatorSet currentListViewAnimation; private PhotoCropView photoCropView; @@ -924,7 +933,7 @@ public void run() { @Override public void run() { if (videoPlayerControlVisible && isPlaying && !ApplicationLoader.mainInterfacePaused) { - if (menuItem != null && menuItem.isSubMenuShowing()) { + if (menuItem != null && menuItem.isSubMenuShowing() || videoItem != null && videoItem.isSubMenuShowing()) { return; } if (captionScrollView != null && captionScrollView.getScrollY() != 0) { @@ -1033,7 +1042,38 @@ public void run() { public final static int SELECT_TYPE_QR = 10; public final static int SELECT_TYPE_STICKER = 11; - VideoPlayerRewinder videoPlayerRewinder = new VideoPlayerRewinder() { + OldVideoPlayerRewinder longVideoPlayerRewinder = new OldVideoPlayerRewinder() { + @Override + protected void onRewindCanceled() { + onTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0)); + videoForwardDrawable.setShowing(false); + + PipVideoOverlay.onRewindCanceled(); + } + + @Override + protected void updateRewindProgressUi(long timeDiff, float progress, boolean rewindByBackSeek) { + videoForwardDrawable.setTime(Math.abs(timeDiff)); + if (rewindByBackSeek) { + videoPlayerSeekbar.setProgress(progress); + videoPlayerSeekbarView.invalidate(); + } + + PipVideoOverlay.onUpdateRewindProgressUi(timeDiff, progress, rewindByBackSeek); + } + + @Override + protected void onRewindStart(boolean rewindForward) { + videoForwardDrawable.setOneShootAnimation(false); + videoForwardDrawable.setLeftSide(!rewindForward); + videoForwardDrawable.setShowing(true); + containerView.invalidate(); + + PipVideoOverlay.onRewindStart(rewindForward); + } + }; + public final VideoFramesRewinder framesRewinder = new VideoFramesRewinder(); + private final VideoPlayerRewinder videoPlayerRewinder = new VideoPlayerRewinder(framesRewinder) { @Override protected void onRewindCanceled() { onTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0)); @@ -1417,7 +1457,7 @@ public void run() { videoPlayerSeekbar.setProgress(progress); } } else { - if (seekToProgressPending == 0 && (videoPlayerRewinder.rewindCount == 0 || !videoPlayerRewinder.rewindByBackSeek)) { + if (seekToProgressPending == 0 && (longVideoPlayerRewinder.rewindCount == 0 || !longVideoPlayerRewinder.rewindByBackSeek && !videoPlayerRewinder.rewindByBackSeek)) { videoPlayerSeekbar.setProgress(progress, false); } if (bufferedProgress != -1) { @@ -1987,26 +2027,28 @@ public interface PageBlocksAdapter { private Rect hitRect = new Rect(); - private final static int gallery_menu_save = 1; - private final static int gallery_menu_showall = 2; - private final static int gallery_menu_send = 3; - private final static int gallery_menu_showinchat = 4; - private final static int gallery_menu_pip = 5; - private final static int gallery_menu_delete = 6; - private final static int gallery_menu_cancel_loading = 7; - private final static int gallery_menu_share = 10; - private final static int gallery_menu_openin = 11; - private final static int gallery_menu_masks = 13; - private final static int gallery_menu_savegif = 14; - private final static int gallery_menu_masks2 = 15; - private final static int gallery_menu_set_as_main = 16; - private final static int gallery_menu_edit_avatar = 17; - private final static int gallery_menu_share2 = 18; - private final static int gallery_menu_speed = 19; - private final static int gallery_menu_paint = 20; - private final static int gallery_menu_translate = 21; - private final static int gallery_menu_hide_translation = 22; - private final static int gallery_menu_reply = 23; + private final static int gallery_menu_quality = 1; + private final static int gallery_menu_save = 2; + private final static int gallery_menu_showall = 3; + private final static int gallery_menu_send = 4; + private final static int gallery_menu_showinchat = 5; + private final static int gallery_menu_pip = 6; + private final static int gallery_menu_delete = 7; + private final static int gallery_menu_cancel_loading = 8; + private final static int gallery_menu_share = 9; + private final static int gallery_menu_openin = 10; + private final static int gallery_menu_masks = 11; + private final static int gallery_menu_savegif = 12; + private final static int gallery_menu_masks2 = 13; + private final static int gallery_menu_set_as_main = 14; + private final static int gallery_menu_edit_avatar = 15; + private final static int gallery_menu_share2 = 16; + private final static int gallery_menu_speed = 17; + private final static int gallery_menu_paint = 18; + private final static int gallery_menu_translate = 19; + private final static int gallery_menu_hide_translation = 20; + private final static int gallery_menu_reply = 21; + private final static int gallery_menu_loop = 22; private final static int ads_sponsor_info = 101; private final static int ads_about = 102; @@ -2787,6 +2829,7 @@ default boolean canLoadMoreAvatars() { return true; } default void onReleasePlayerBeforeClose(int currentIndex) {}; + default long getDialogId() { return 0; } default boolean forceAllInGroup() { return false; @@ -3073,10 +3116,16 @@ protected void onDraw(Canvas canvas) { public void draw(Canvas canvas) { super.draw(canvas); - if (photoViewerWebView != null && photoViewerWebView.isControllable() && videoForwardDrawable != null && videoForwardDrawable.isAnimating()) { + if (photoViewerWebView != null && photoViewerWebView.isControllable()) { int h = (int) (photoViewerWebView.getWebView().getMeasuredHeight() * (scale - 1.0f)) / 2; - videoForwardDrawable.setBounds(photoViewerWebView.getLeft(), photoViewerWebView.getWebView().getTop() - h + (int) (translationY / scale), photoViewerWebView.getRight(), photoViewerWebView.getWebView().getBottom() + h + (int) (translationY / scale)); - videoForwardDrawable.draw(canvas); + if (videoForwardDrawable != null && videoForwardDrawable.isAnimating()) { + videoForwardDrawable.setBounds(photoViewerWebView.getLeft(), photoViewerWebView.getWebView().getTop() - h + (int) (translationY / scale), photoViewerWebView.getRight(), photoViewerWebView.getWebView().getBottom() + h + (int) (translationY / scale)); + videoForwardDrawable.draw(canvas); + } + if (seekSpeedDrawable != null && seekSpeedDrawable.isShown()) { + seekSpeedDrawable.setBounds(photoViewerWebView.getLeft(), (int) (AndroidUtilities.statusBarHeight + dp(90) * actionBar.getAlpha()), photoViewerWebView.getRight(), photoViewerWebView.getWebView().getBottom() + h + (int) (translationY / scale)); + seekSpeedDrawable.draw(canvas); + } } } @@ -4625,6 +4674,9 @@ protected void onLayout(boolean changed, int _l, int t, int _r, int _b) { } }; containerView.setFocusable(false); + if (framesRewinder != null) { + framesRewinder.setParentView(containerView); + } containerView.setClipChildren(true); containerView.setClipToPadding(true); @@ -5280,7 +5332,7 @@ public void dismiss() { }; masksAlert.show(); } else if (id == gallery_menu_pip) { - if (pipItem.getAlpha() != 1.0f) { + if (!menuItem.isSubItemVisible(gallery_menu_pip)) { return; } if (isEmbedVideo) { @@ -5453,6 +5505,13 @@ public void dismiss() { menuItem.hideSubItem(gallery_menu_hide_translation); }, 32); updateCaptionTranslated(); + } else if (id == gallery_menu_loop) { + playerLooping = !playerLooping; + VideoPlayer.saveLooping(playerLooping, currentMessageObject); + if (videoPlayer != null) { + videoPlayer.setLooping(playerLooping); + } + loopItem.setEnabledByColor(playerLooping, 0xFFFFFFFF, 0xFF73B4EC); } } @@ -5476,14 +5535,42 @@ public boolean canOpenMenu() { masksItem = menu.addItem(gallery_menu_masks, R.drawable.msg_mask); masksItem.setContentDescription(getString("Masks", R.string.Masks)); - pipItem = menu.addItem(gallery_menu_pip, R.drawable.ic_goinline); - pipItem.setContentDescription(getString("AccDescrPipMode", R.string.AccDescrPipMode)); editItem = menu.addItem(gallery_menu_paint, R.drawable.msg_header_draw); editItem.setContentDescription(getString("AccDescrPhotoEditor", R.string.AccDescrPhotoEditor)); sendItem = menu.addItem(gallery_menu_send, R.drawable.msg_header_share); sendItem.setContentDescription(getString("Forward", R.string.Forward)); - menuItem = menu.addItem(0, menuItemIcon = new OptionsSpeedIconDrawable()); + videoItem = menu.addItem(gallery_menu_quality, videoItemIcon = new ChooseQualityLayout.QualityIcon(activityContext)); + videoItemIcon.setCallback(videoItem.getIconView()); + videoItem.getPopupLayout().setSwipeBackForegroundColor(0xff222222); + videoItem.getPopupLayout().swipeBackGravityRight = true; + videoItem.getPopupLayout().setFitItems(true); + videoItem.setMenuXOffset(dp(3)); + + speedItem = new ActionBarMenuSlider.SpeedSlider(activityContext, resourcesProvider); + speedItem.setStops(new float[] { 0.5f, 1.0f, 1.5f, 2.0f, 2.5f }); + speedItem.setMinimumWidth(AndroidUtilities.dp(196)); + speedItem.setDrawShadow(false); + speedItem.setBackgroundColor(0xff222222); + speedItem.setTextColor(0xffffffff); + speedItem.setLabel(LocaleController.getString(R.string.VideoPlayerSpeed)); + speedItem.setOnValueChange((value, isFinal) -> { + final float speed = ActionBarMenuSlider.SpeedSlider.MIN_SPEED + (ActionBarMenuSlider.SpeedSlider.MAX_SPEED - ActionBarMenuSlider.SpeedSlider.MIN_SPEED) * value; + chooseSpeed(speed, isFinal, false); + }); + videoItem.getPopupLayout().addView(speedItem, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 44)); + speedGap = videoItem.addColoredGap(); + speedGap.setColor(0xff181818); + videoItem.getPopupLayout().addView(chooseSpeedLayout = new SpeedButtonsLayout(activityContext, this::chooseSpeed)); + videoQualityLayout = new LinearLayout(activityContext); + videoQualityLayout.setOrientation(LinearLayout.VERTICAL); + videoItem.getPopupLayout().addView(videoQualityLayout); + loopItem = videoItem.addSubItem(gallery_menu_loop, R.drawable.menu_video_loop, LocaleController.getString(R.string.VideoPlayerLoop)); + videoItem.redrawPopup(0xf9222222); + videoItem.setOnMenuDismiss(byClick -> checkProgress(0, false, false)); + + menuItem = menu.addItem(0, R.drawable.media_more); + menuItem.setContentDescription(getString(R.string.AccDescrMoreOptions)); menuItem.setOnClickListener(v -> { if (currentMessageObject != null && currentMessageObject.isSponsored()) { openAdsMenu(); @@ -5493,28 +5580,10 @@ public boolean canOpenMenu() { }); menuItem.setOnMenuDismiss(byClick -> checkProgress(0, false, false)); + menuItem.getPopupLayout().setSwipeBackForegroundColor(0xff222222); menuItem.getPopupLayout().swipeBackGravityRight = true; + menuItem.getPopupLayout().setFitItems(true); - chooseQualityLayout = new ChooseQualityLayout(activityContext, menuItem.getPopupLayout().getSwipeBack(), (qualityIndex, isFinal, closeMenu) -> { - if (videoPlayer != null) { - videoPlayer.setSelectedQuality(qualityIndex); - } - if (qualityIndex == VideoPlayer.QUALITY_AUTO) { - VideoPlayer.saveQuality(null, currentMessageObject); - } else { - VideoPlayer.saveQuality(videoPlayer.getQuality(qualityIndex), currentMessageObject); - } - updateQualityItems(); - if (closeMenu) { - menuItem.toggleSubMenu(); - } - }); - qualityItem = menuItem.addSwipeBackItem(0, qualityIcon = new ChooseQualityLayout.QualityIcon(activityContext), getString(R.string.Quality), chooseQualityLayout.layout); - qualityItem.setColors(0xfffafafa, 0xfffafafa); - qualityItem.setVisibility(View.GONE); - menuItem.getPopupLayout().getSwipeBack().addOnSwipeBackProgressListener((layout, to, t) -> { - qualityIcon.setRotation(t); - }); chooseDownloadQualityLayout = new ChooseDownloadQualityLayout(activityContext, menuItem.getPopupLayout().getSwipeBack(), (messageObject, quality) -> { if (quality == null) return; TLRPC.Document document = quality.getDownloadDocument(); @@ -5541,45 +5610,7 @@ public boolean canOpenMenu() { menuItem.toggleSubMenu(); }); - chooseSpeedLayout = new ChooseSpeedLayout(activityContext, menuItem.getPopupLayout().getSwipeBack(), (speed, isFinal, closeMenu) -> { - if (speed != currentVideoSpeed) { - currentVideoSpeed = speed; - if (currentMessageObject != null) { - SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("playback_speed", Activity.MODE_PRIVATE); - if (Math.abs(currentVideoSpeed - 1.0f) < 0.001f) { - preferences.edit().remove("speed" + currentMessageObject.getDialogId() + "_" + currentMessageObject.getId()).commit(); - } else { - preferences.edit().putFloat("speed" + currentMessageObject.getDialogId() + "_" + currentMessageObject.getId(), currentVideoSpeed).commit(); - } - } - if (videoPlayer != null) { - videoPlayer.setPlaybackSpeed(currentVideoSpeed); - } - if (photoViewerWebView != null) { - photoViewerWebView.setPlaybackSpeed(currentVideoSpeed); - } - } - setMenuItemIcon(true, isFinal); - if (closeMenu) { - menuItem.toggleSubMenu(); - } - }); - speedItem = menuItem.addSwipeBackItem(R.drawable.msg_speed, null, getString(R.string.Speed), chooseSpeedLayout.speedSwipeBackLayout); - menuItem.getPopupLayout().setSwipeBackForegroundColor(0xff222222); - speedItem.setSubtext(getString(R.string.SpeedNormal)); - speedItem.setColors(0xfffafafa, 0xfffafafa); - speedGap = menuItem.addColoredGap(); - speedGap.setColor(0xff181818); - menuItem.getPopupLayout().setFitItems(true); - - menuItem.addSubItem(gallery_menu_openin, R.drawable.msg_openin, getString(R.string.OpenInExternalApp)).setColors(0xfffafafa, 0xfffafafa); - menuItem.setContentDescription(getString(R.string.AccDescrMoreOptions)); - allMediaItem = menuItem.addSubItem(gallery_menu_showall, R.drawable.msg_media, getString(R.string.ShowAllMedia)); - allMediaItem.setColors(0xfffafafa, 0xfffafafa); - menuItem.addSubItem(gallery_menu_savegif, R.drawable.msg_gif, getString(R.string.SaveToGIFs)).setColors(0xfffafafa, 0xfffafafa); - menuItem.addSubItem(gallery_menu_showinchat, R.drawable.msg_message, getString(R.string.ShowInChat)).setColors(0xfffafafa, 0xfffafafa); - ActionBarMenuSubItem galleryButton = menuItem.addSwipeBackItem(R.drawable.msg_gallery, null, getString(R.string.SaveToGallery), chooseDownloadQualityLayout.layout).setColors(0xfffafafa, 0xfffafafa); - galleryButton.setRightIcon(0); + galleryButton = menuItem.addSwipeBackItem(R.drawable.msg_gallery, null, getString(R.string.SaveToGallery), chooseDownloadQualityLayout.layout).setColors(0xfffafafa, 0xfffafafa); galleryButton.setOnClickListener(v -> { if (currentMessageObject != null && currentMessageObject.hasVideoQualities() && chooseDownloadQualityLayout.update(currentMessageObject)) { galleryButton.openSwipeBack(); @@ -5590,6 +5621,14 @@ public boolean canOpenMenu() { menuItem.toggleSubMenu(); } }); + galleryGap = menuItem.addColoredGap(); + galleryGap.setColor(0xff181818); + menuItem.addSubItem(gallery_menu_openin, R.drawable.msg_openin, getString(R.string.OpenInExternalApp)).setColors(0xfffafafa, 0xfffafafa); + pipItem = menuItem.addSubItem(gallery_menu_pip, R.drawable.menu_video_pip, getString(R.string.PipMinimize)).setColors(0xfffafafa, 0xfffafafa); + allMediaItem = menuItem.addSubItem(gallery_menu_showall, R.drawable.msg_media, getString(R.string.ShowAllMedia)); + allMediaItem.setColors(0xfffafafa, 0xfffafafa); + menuItem.addSubItem(gallery_menu_savegif, R.drawable.msg_gif, getString(R.string.SaveToGIFs)).setColors(0xfffafafa, 0xfffafafa); + menuItem.addSubItem(gallery_menu_showinchat, R.drawable.msg_message, getString(R.string.ShowInChat)).setColors(0xfffafafa, 0xfffafafa); menuItem.addSubItem(gallery_menu_reply, R.drawable.menu_reply, getString(R.string.Reply)).setColors(0xfffafafa, 0xfffafafa); menuItem.addSubItem(gallery_menu_share, R.drawable.msg_shareout, getString(R.string.ShareFile)).setColors(0xfffafafa, 0xfffafafa); menuItem.addSubItem(gallery_menu_masks2, R.drawable.msg_sticker, getString(R.string.ShowStickers)).setColors(0xfffafafa, 0xfffafafa); @@ -5850,6 +5889,8 @@ public void invalidate() { } }); + seekSpeedDrawable = new SeekSpeedDrawable(containerView::invalidate, false, false); + qualityChooseView = new QualityChooseView(parentActivity); qualityChooseView.setTranslationY(dp(120)); qualityChooseView.setVisibility(View.INVISIBLE); @@ -7576,65 +7617,223 @@ private void updateActionBarTitlePadding() { private void setMenuItemIcon(boolean animated, boolean isFinal) { if (speedItem.getVisibility() != View.VISIBLE) { - menuItemIcon.setSpeed(null, animated); - return; - } - menuItemIcon.setSpeed(Math.abs(currentVideoSpeed - 1f) < 0.001f ? null : currentVideoSpeed, animated); - if (isFinal) { - if (Math.abs(currentVideoSpeed - 0.2f) < 0.05f) { - speedItem.setSubtext(getString(R.string.VideoSpeedVerySlow)); - } else if (Math.abs(currentVideoSpeed - 0.5f) < 0.05f) { - speedItem.setSubtext(getString(R.string.VideoSpeedSlow)); - } else if (Math.abs(currentVideoSpeed - 1.0f) < 0.05f) { - speedItem.setSubtext(getString(R.string.VideoSpeedNormal)); - } else if (Math.abs(currentVideoSpeed - 1.5f) < 0.05f) { - speedItem.setSubtext(getString(R.string.VideoSpeedFast)); - } else if (Math.abs(currentVideoSpeed - 2f) < 0.05f) { - speedItem.setSubtext(getString(R.string.VideoSpeedVeryFast)); + videoItemIcon.topText.setText("", animated); + } else { + if (Math.abs(currentVideoSpeed - 1f) < 0.001f) { + videoItemIcon.topText.setText("", animated); } else { - speedItem.setSubtext(LocaleController.formatString(R.string.VideoSpeedCustom, SpeedIconDrawable.formatNumber(currentVideoSpeed) + "x")); + videoItemIcon.topText.setText(SpeedIconDrawable.formatNumber(currentVideoSpeed) + "x", animated); } } + speedItem.setSpeed(currentVideoSpeed, animated); chooseSpeedLayout.update(currentVideoSpeed, isFinal); } + private void chooseQuality(int qualityIndex) { + if (videoPlayer != null) { + videoPlayer.setSelectedQuality(qualityIndex); + } + if (qualityIndex == VideoPlayer.QUALITY_AUTO) { + VideoPlayer.saveQuality(null, currentMessageObject); + } else if (videoPlayer != null) { + VideoPlayer.saveQuality(videoPlayer.getQuality(qualityIndex), currentMessageObject); + } + updateQualityItems(); + videoItem.toggleSubMenu(); + } + private void updateQualityItems() { - if (chooseQualityLayout == null || qualityItem == null) return; - if (chooseQualityLayout.update(videoPlayer)) { - qualityItem.setVisibility(View.VISIBLE); - if (videoPlayer.getSelectedQuality() == VideoPlayer.QUALITY_AUTO) { - qualityItem.setSubtext(getString(R.string.QualityAuto)); - } else { - final VideoPlayer.Quality q = videoPlayer.getQuality(videoPlayer.getSelectedQuality()); - qualityItem.setSubtext(q != null ? Math.min(q.width, q.height) + "p" : ""); - } - final VideoPlayer.Quality q = videoPlayer.getCurrentQuality(); - if (q != null) { - final int max = Math.max(q.width, q.height); - int p = Math.min(q.width, q.height); - if (Math.abs(p - 1080) < 30) p = 1080; - else if (Math.abs(p - 720) < 30) p = 720; - else if (Math.abs(p - 360) < 30) p = 360; - else if (Math.abs(p - 240) < 30) p = 240; - else if (Math.abs(p - 144) < 30) p = 144; - if (max >= 16000) { - qualityIcon.text.setText("16K"); - } else if (max >= 8000) { - qualityIcon.text.setText("8K"); - } else if (max >= 4000) { - qualityIcon.text.setText("4K"); - } else if (p >= 1080) { - qualityIcon.text.setText("HD"); + if (videoPlayer == null || videoPlayer.getQualitiesCount() <= 1) { + videoQualityLayout.setVisibility(View.GONE); + chooseSpeedLayout.setVisibility(View.VISIBLE); +// videoItem.setVisibility(View.GONE); + galleryButton.setRightIcon(0); + videoItemIcon.bottomText.setText("", true); + return; + } + galleryButton.setRightIcon(R.drawable.msg_arrowright); +// videoItem.setVisibility(View.VISIBLE); + chooseSpeedLayout.setVisibility(View.GONE); + videoQualityLayout.setVisibility(View.VISIBLE); + final VideoPlayer.Quality currentQuality = videoPlayer.getCurrentQuality(); + if (1 + videoPlayer.getQualitiesCount() != videoQualityItems.size()) { + videoQualityLayout.removeAllViews(); + videoQualityItems.clear(); + + TextView header = new TextView(activityContext); + header.setText(LocaleController.getString(R.string.QualityList)); + header.setTypeface(AndroidUtilities.bold()); + header.setPadding(dp(16), dp(9), dp(16), dp(8)); + header.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + header.setTextColor(0xFFFFFFFF); + videoQualityLayout.addView(header, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + for (int i = -1; i < videoPlayer.getQualitiesCount(); ++i) { + SpannableStringBuilder title = new SpannableStringBuilder(); + VideoPlayer.Quality q; + boolean visible = true; + if (i == -1) { + q = null; + title.append(LocaleController.getString(R.string.QualityAuto)); + if (i == videoPlayer.getSelectedQuality() && currentQuality != null) { + q = currentQuality; + title.append(" "); + int start = title.length(); + if (SharedConfig.debugVideoQualities) { + title.append(Integer.toString(q.width)).append("x").append(Integer.toString(q.height)); + } else { + title.append(String.valueOf(q.p())).append("p"); + } + title.setSpan(new ForegroundColorSpan(Theme.multAlpha(0xFFFFFFFF, .5f)), start, title.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } } else { - qualityIcon.text.setText(p + ""); + q = videoPlayer.getQuality(i); + if (q.original) { + title.append(LocaleController.getString(R.string.QualityOriginal)); +// if (i != videoPlayer.getSelectedQuality() && videoPlayer.getQualitiesCount() >= 2 && (q.uris.isEmpty() || !q.uris.get(0).isCached())) { +// visible = false; +// } + } else if (q.p() >= 2000) { + title.append(LocaleController.getString(R.string.Quality2160)); + } else if (q.p() >= 1400) { + title.append(LocaleController.getString(R.string.Quality1440)); + } else if (q.p() >= 1000) { + title.append(LocaleController.getString(R.string.Quality1080)); + } else if (q.p() >= 700) { + title.append(LocaleController.getString(R.string.Quality720)); + } else if (q.p() >= 400) { + title.append(LocaleController.getString(R.string.Quality480)); + } else if (q.p() >= 340) { + title.append(LocaleController.getString(R.string.Quality360)); + } else if (q.p() >= 200) { + title.append(LocaleController.getString(R.string.Quality240)); + } else { + title.append(LocaleController.getString(R.string.Quality144)); + } + title.append(" "); + int start = title.length(); + if (SharedConfig.debugVideoQualities) { + title.append(Integer.toString(q.width)).append("x").append(Integer.toString(q.height)); + } else { + title.append(String.valueOf(q.p())).append("p"); + } + title.setSpan(new ForegroundColorSpan(Theme.multAlpha(0xFFFFFFFF, .5f)), start, title.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } - } else { - qualityIcon.text.setText(""); + ActionBarMenuSubItem item = ActionBarMenuItem.addItem(videoQualityLayout, 0, title, true, null); + if (SharedConfig.debugVideoQualities && q != null) { + StringBuilder subtext = new StringBuilder(); + for (VideoPlayer.VideoUri uri : q.uris) { + if (subtext.length() > 0) subtext.append("\n"); + if (!TextUtils.isEmpty(uri.codec)) subtext.append(uri.codec).append(", "); + subtext.append(AndroidUtilities.formatFileSize(uri.size).replace(" ", "")).append("("); + subtext.append(AndroidUtilities.formatFileSize((long) uri.bitrate).replace(" ", "") + "/s)"); + subtext.append(uri.isManifestCached() ? "!" : " ").append(uri.isCached() ? "!" : " "); + } + item.setSubtext(subtext.toString()); + } else { + item.setSubtext(""); + } + item.setChecked(i == videoPlayer.getSelectedQuality()); + item.setColors(0xfffafafa, 0xfffafafa); + final int index = i; + item.setVisibility(visible ? View.VISIBLE : View.GONE); + item.setOnClickListener((view) -> { + chooseQuality(index); + }); + item.setSelectorColor(0x0fffffff); + videoQualityItems.add(item); } + ActionBarPopupWindow.GapView gap = new ActionBarPopupWindow.GapView(activityContext, resourcesProvider, Theme.key_actionBarDefaultSubmenuSeparator); + gap.setTag(R.id.fit_width_tag, 1); + gap.setColor(0xff181818); + videoQualityLayout.addView(gap, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 8)); } else { - qualityItem.setVisibility(View.GONE); - qualityIcon.text.setText(""); + for (int i = -1; i < videoPlayer.getQualitiesCount(); ++i) { + ActionBarMenuSubItem item = videoQualityItems.get(i + 1); + SpannableStringBuilder title = new SpannableStringBuilder(); + VideoPlayer.Quality q; + boolean visible = true; + if (i == -1) { + q = null; + title.append(LocaleController.getString(R.string.QualityAuto)); + if (i == videoPlayer.getSelectedQuality() && currentQuality != null) { + q = currentQuality; + title.append(" "); + int start = title.length(); + if (SharedConfig.debugVideoQualities) { + title.append(Integer.toString(q.width)).append("x").append(Integer.toString(q.height)); + } else { + title.append(String.valueOf(q.p())).append("p"); + } + title.setSpan(new ForegroundColorSpan(Theme.multAlpha(0xFFFFFFFF, .5f)), start, title.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } else { + q = videoPlayer.getQuality(i); + if (q.original) { + title.append(LocaleController.getString(R.string.QualityOriginal)); +// if (i != videoPlayer.getSelectedQuality() && videoPlayer.getQualitiesCount() >= 2 && (q.uris.isEmpty() || !q.uris.get(0).isCached())) { +// visible = false; +// } + } else if (q.p() >= 2000) { + title.append(LocaleController.getString(R.string.Quality2160)); + } else if (q.p() >= 1400) { + title.append(LocaleController.getString(R.string.Quality1440)); + } else if (q.p() >= 1000) { + title.append(LocaleController.getString(R.string.Quality1080)); + } else if (q.p() >= 700) { + title.append(LocaleController.getString(R.string.Quality720)); + } else if (q.p() >= 400) { + title.append(LocaleController.getString(R.string.Quality480)); + } else if (q.p() >= 340) { + title.append(LocaleController.getString(R.string.Quality360)); + } else if (q.p() >= 200) { + title.append(LocaleController.getString(R.string.Quality240)); + } else { + title.append(LocaleController.getString(R.string.Quality144)); + } + title.append(" "); + int start = title.length(); + if (SharedConfig.debugVideoQualities) { + title.append(Integer.toString(q.width)).append("x").append(Integer.toString(q.height)); + } else { + title.append(String.valueOf(q.p())).append("p"); + } + title.setSpan(new ForegroundColorSpan(Theme.multAlpha(0xFFFFFFFF, .5f)), start, title.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + item.setText(title); + item.setVisibility(visible ? View.VISIBLE : View.GONE); + if (SharedConfig.debugVideoQualities && q != null) { + StringBuilder subtext = new StringBuilder(); + for (VideoPlayer.VideoUri uri : q.uris) { + if (subtext.length() > 0) subtext.append("\n"); + if (!TextUtils.isEmpty(uri.codec)) subtext.append(uri.codec).append(", "); + subtext.append(AndroidUtilities.formatFileSize(uri.size).replace(" ", "")).append(" ("); + subtext.append(AndroidUtilities.formatFileSize((long) uri.bitrate).replace(" ", "") + "/s)"); + subtext.append(uri.isManifestCached() ? "!" : " ").append(uri.isCached() ? "!" : " "); + } + item.setSubtext(subtext.toString()); + } else { + item.setSubtext(""); + } + item.setChecked(i == videoPlayer.getSelectedQuality()); + final int index = i; + item.setOnClickListener((view) -> { + chooseQuality(index); + }); + } + } + String t = ""; + if (currentQuality != null) { + if (currentQuality.p() >= 2000) { + t = LocaleController.getString(R.string.Quality2160Short); + } else if (currentQuality.p() >= 1000) { + t = LocaleController.getString(R.string.Quality1080Short); + } else if (currentQuality.p() >= 700) { + t = LocaleController.getString(R.string.Quality720Short); + } else { + t = currentQuality.p() + "p"; + } } + videoItemIcon.bottomText.setText(t, true); } public float getCurrentVideoSpeed() { @@ -8447,7 +8646,7 @@ private void onHideView() { } private void onUserLeaveHint() { - if (pipItem.getAlpha() != 1.0f || !AndroidUtilities.checkInlinePermissions(parentActivity) || PipVideoOverlay.isVisible() || !isPlaying) { + if (!pipItem.isEnabled() || !AndroidUtilities.checkInlinePermissions(parentActivity) || PipVideoOverlay.isVisible() || !isPlaying) { return; } if (isEmbedVideo) { @@ -9584,9 +9783,15 @@ public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { videoPlayer.setPlayWhenReady(playWhenReady); } - playerLooping = (currentMessageObject != null && currentMessageObject.getDuration() <= 30) || (pageBlocksAdapter != null && pageBlocksAdapter.isHardwarePlayer(currentIndex)); + Boolean savedLooping = VideoPlayer.getLooping(currentMessageObject); + if (savedLooping != null) { + playerLooping = savedLooping; + } else { + playerLooping = (currentMessageObject != null && currentMessageObject.getDuration() <= 30) || (pageBlocksAdapter != null && pageBlocksAdapter.isHardwarePlayer(currentIndex)); + } videoPlayerControlFrameLayout.setSeekBarTransitionEnabled(playerLooping); videoPlayer.setLooping(playerLooping); + loopItem.setEnabledByColor(playerLooping, 0xFFFFFFFF, 0xFF73B4EC); if (currentMessageObject != null && currentMessageObject.forceSeekTo >= 0) { seekToProgressPending = currentMessageObject.forceSeekTo; @@ -12660,7 +12865,7 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca menuItem.hideSubItem(gallery_menu_delete); speedItem.setVisibility(View.GONE); speedGap.setVisibility(View.GONE); - qualityItem.setVisibility(View.GONE); + videoItem.setVisibility(View.GONE); actionBar.setTranslationY(0); dialogPhotos = null; @@ -13245,7 +13450,7 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated menuItem.checkHideMenuItem(); } else { speedItem.setVisibility(View.GONE); - qualityItem.setVisibility(View.GONE); + videoItem.setVisibility(View.GONE); speedGap.setVisibility(View.GONE); menuItem.hideSubItem(gallery_menu_openin); menuItem.checkHideMenuItem(); @@ -13269,7 +13474,8 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated } if (currentAnimation != null) { - menuItem.hideSubItem(gallery_menu_save); + galleryButton.setVisibility(View.GONE); + galleryGap.setVisibility(View.GONE); menuItem.hideSubItem(gallery_menu_share); setItemVisible(editItem, false, animated); if (!newMessageObject.canDeleteMessage(parentChatActivity != null && parentChatActivity.isInScheduleMode(), null)) { @@ -13380,19 +13586,22 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated } if (isEmbedVideo || newMessageObject.messageOwner.ttl != 0 && newMessageObject.messageOwner.ttl < 60 * 60 || noforwards) { allowShare = false; - menuItem.hideSubItem(gallery_menu_save); + galleryButton.setVisibility(View.GONE); + galleryGap.setVisibility(View.GONE); menuItem.hideSubItem(gallery_menu_share); setItemVisible(editItem, false, animated); } else { allowShare = true; - menuItem.showSubItem(gallery_menu_save); + galleryButton.setVisibility(View.VISIBLE); + galleryGap.setVisibility(View.VISIBLE); menuItem.showSubItem(gallery_menu_share); } groupedPhotosListView.fillList(); } else if (!secureDocuments.isEmpty()) { allowShare = false; menuItem.showSubItem(gallery_menu_delete); - menuItem.hideSubItem(gallery_menu_save); + galleryButton.setVisibility(View.GONE); + galleryGap.setVisibility(View.GONE); menuItem.hideSubItem(gallery_menu_translate); menuItem.hideSubItem(gallery_menu_hide_translation); if (countView != null) { @@ -13463,9 +13672,11 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated } boolean noforwards = avatarsDialogId != 0 && MessagesController.getInstance(currentAccount).isChatNoForwards(-avatarsDialogId); if (noforwards) { - menuItem.hideSubItem(gallery_menu_save); + galleryButton.setVisibility(View.GONE); + galleryGap.setVisibility(View.GONE); } else { - menuItem.showSubItem(gallery_menu_save); + galleryButton.setVisibility(View.VISIBLE); + galleryGap.setVisibility(View.VISIBLE); } allowShare = !noforwards; menuItem.showSubItem(gallery_menu_share); @@ -13727,7 +13938,8 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated countView.set(switchingToIndex + 1, size); } if (currentAnimation != null || (!pageBlocksAdapter.isVideo(index) && pageBlocksAdapter.isHardwarePlayer(index))) { - menuItem.hideSubItem(gallery_menu_save); + galleryButton.setVisibility(View.GONE); + galleryGap.setVisibility(View.GONE); if (allowShare) { menuItem.showSubItem(gallery_menu_savegif); } else { @@ -13743,7 +13955,8 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated title = getString("AttachPhoto", R.string.AttachPhoto); } } - menuItem.showSubItem(gallery_menu_save); + galleryButton.setVisibility(View.VISIBLE); + galleryGap.setVisibility(View.VISIBLE); menuItem.hideSubItem(gallery_menu_savegif); menuItem.checkHideMenuItem(); } @@ -14147,22 +14360,27 @@ private void setImageIndex(int index, boolean init, boolean animateCaption) { if (sharedMediaType == MediaDataController.MEDIA_FILE) { if (canZoom = newMessageObject.canPreviewDocument()) { if (allowShare) { - menuItem.showSubItem(gallery_menu_save); + galleryButton.setVisibility(View.VISIBLE); + galleryGap.setVisibility(View.VISIBLE); } else { - menuItem.hideSubItem(gallery_menu_save); + galleryButton.setVisibility(View.GONE); + galleryGap.setVisibility(View.GONE); } setDoubleTapEnabled(true); } else { - menuItem.hideSubItem(gallery_menu_save); + galleryButton.setVisibility(View.GONE); + galleryGap.setVisibility(View.GONE); setDoubleTapEnabled(false); } } if (isVideo || isEmbedVideo) { speedItem.setVisibility(View.VISIBLE); + videoItem.setVisibility(View.VISIBLE); menuItem.showSubItem(gallery_menu_speed); speedGap.setVisibility(menuItem.getVisibleSubItemsCount() > 1 ? View.VISIBLE : View.GONE); } else { speedItem.setVisibility(View.GONE); + videoItem.setVisibility(View.GONE); speedGap.setVisibility(View.GONE); menuItem.checkHideMenuItem(); } @@ -14464,6 +14682,14 @@ private void setImageIndex(int index, boolean init, boolean animateCaption) { } } detectFaces(); + if (captionEdit != null) { + long dialogId = 0; + if (placeProvider != null) + dialogId = placeProvider.getDialogId(); + if (dialogId == 0 && currentMessageObject != null) + dialogId = currentMessageObject.getDialogId(); + captionEdit.setDialogId(dialogId); + } } private void resetIndexForDeferredImageLoading() { @@ -14827,7 +15053,7 @@ private void checkProgress(int a, boolean scroll, boolean animated) { f2Resolver = () -> FileLoader.getInstance(currentAccount).getPathToMessage(finalMessage); } if (messageObject.isVideo()) { - canStream = SharedConfig.streamMedia && messageObject.canStreamVideo() && !DialogObject.isEncryptedDialog(messageObject.getDialogId()); + canStream = SharedConfig.streamMedia && messageObject.canStreamVideo() && !DialogObject.isEncryptedDialog(messageObject.getDialogId()) || messageObject.hasVideoQualities(); isVideo = true; } } else if (currentBotInlineResult != null) { @@ -17107,10 +17333,12 @@ private int getContainerViewHeight(boolean trueHeight, int mode) { return height; } + float lastX; float longPressX; Runnable longPressRunnable = this::onLongPress; private boolean onTouchEvent(MotionEvent ev) { + lastX = ev.getX(); if (currentEditMode == EDIT_MODE_PAINT && animationStartTime != 0 && (ev.getActionMasked() == MotionEvent.ACTION_DOWN || ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN)) { if (ev.getPointerCount() >= 2) { cancelMoveZoomAnimation(); @@ -17122,12 +17350,20 @@ private boolean onTouchEvent(MotionEvent ev) { return false; } - if (videoPlayerRewinder.rewindCount > 0) { + if (longVideoPlayerRewinder.rewindCount > 0) { if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL ) { - videoPlayerRewinder.cancelRewind(); + longVideoPlayerRewinder.cancelRewind(); return false; } return true; + } else if (videoPlayerRewinder.rewinding) { + if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) { + videoPlayerRewinder.cancelRewind(); + return false; + } else if (ev.getAction() == MotionEvent.ACTION_MOVE) { + videoPlayerRewinder.setX(ev.getX()); + return true; + } } if (currentEditMode == EDIT_MODE_FILTER) { @@ -17239,6 +17475,9 @@ private boolean onTouchEvent(MotionEvent ev) { AndroidUtilities.cancelRunOnUIThread(longPressRunnable); } } else if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) { + if (Math.abs(longPressX - lastX) > AndroidUtilities.touchSlop) { + AndroidUtilities.cancelRunOnUIThread(longPressRunnable); + } if (canZoom && ev.getPointerCount() == 2 && !draggingDown && zooming && !changingPage) { discardTap = true; @@ -17616,7 +17855,7 @@ private void switchToNextIndex(int add, boolean init) { } private boolean shouldMessageObjectAutoPlayed(MessageObject messageObject) { - return messageObject != null && messageObject.isVideo() && (messageObject.mediaExists || messageObject.attachPathExists || messageObject.canStreamVideo() && SharedConfig.streamMedia) && SharedConfig.isAutoplayVideo(); + return messageObject != null && messageObject.isVideo() && (messageObject.mediaExists || messageObject.attachPathExists || messageObject.hasVideoQualities() || messageObject.canStreamVideo() && SharedConfig.streamMedia) && SharedConfig.isAutoplayVideo(); } private boolean shouldIndexAutoPlayed(int index) { @@ -17965,18 +18204,20 @@ public void onAnimationEnd(Animator animation) { } groupedPhotosListView.setMoveProgress(-alpha); - canvas.save(); - canvas.translate(translateX, 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); + if (seekSpeedDrawable == null || !seekSpeedDrawable.isShown()) { + canvas.save(); + canvas.translate(translateX, 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); - if (isActionBarVisible) { - fullscreenButton[1].setAlpha(alpha); - } + if (isActionBarVisible) { + fullscreenButton[1].setAlpha(alpha); + } - canvas.restore(); + canvas.restore(); + } } else { if (isActionBarVisible) { fullscreenButton[1].setAlpha(0.0f); @@ -18251,6 +18492,9 @@ public void onAnimationEnd(Animator animation) { } if (!usedSurfaceView || firstFrameRendered) { aspectRatioFrameLayout.draw(canvas); + if (framesRewinder != null) { + framesRewinder.draw(canvas, aspectRatioFrameLayout.getWidth(), aspectRatioFrameLayout.getHeight()); + } } if (usedSurfaceView && alpha != 1f) { if (surfaceBlackoutPaint == null) { @@ -18368,17 +18612,19 @@ public void onAnimationEnd(Animator animation) { } groupedPhotosListView.setMoveProgress(1.0f - alpha); - canvas.save(); - canvas.translate(currentTranslationX, 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); + if (seekSpeedDrawable == null || !seekSpeedDrawable.isShown()) { + canvas.save(); + canvas.translate(currentTranslationX, 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); - if (isActionBarVisible) { - fullscreenButton[2].setAlpha(1.0f); + if (isActionBarVisible) { + fullscreenButton[2].setAlpha(1.0f); + } + canvas.restore(); } - canvas.restore(); } else { if (isActionBarVisible) { fullscreenButton[2].setAlpha(0.0f); @@ -18426,10 +18672,16 @@ public void onAnimationEnd(Animator animation) { canvas.restore(); } - if (aspectRatioFrameLayout != null && videoForwardDrawable.isAnimating()) { - int h = (int) (aspectRatioFrameLayout.getMeasuredHeight() * (scale - 1.0f)) / 2; - videoForwardDrawable.setBounds(aspectRatioFrameLayout.getLeft(), aspectRatioFrameLayout.getTop() - h + (int) (currentTranslationY / currentScale), aspectRatioFrameLayout.getRight(), aspectRatioFrameLayout.getBottom() + h + (int) (currentTranslationY / currentScale)); - videoForwardDrawable.draw(canvas); + if (aspectRatioFrameLayout != null) { + int h = (int) (aspectRatioFrameLayout.getMeasuredHeight() * (currentScale - 1.0f)) / 2; + if (videoForwardDrawable.isAnimating()) { + videoForwardDrawable.setBounds(aspectRatioFrameLayout.getLeft(), aspectRatioFrameLayout.getTop() - h + (int) (currentTranslationY / currentScale), aspectRatioFrameLayout.getRight(), aspectRatioFrameLayout.getBottom() + h + (int) (currentTranslationY / currentScale)); + videoForwardDrawable.draw(canvas); + } + if (seekSpeedDrawable.isShown()) { + seekSpeedDrawable.setBounds(aspectRatioFrameLayout.getLeft(), (int) (AndroidUtilities.statusBarHeight + dp(90) * actionBar.getAlpha()), aspectRatioFrameLayout.getRight(), aspectRatioFrameLayout.getBottom() + h + (int) (currentTranslationY / currentScale)); + seekSpeedDrawable.draw(canvas); + } } if (BLUR_RENDERNODE()) { @@ -18553,17 +18805,19 @@ private void drawProgress(Canvas canvas, float translateX, float currentScale, f } else if (photoProgressViews[0].backgroundState == PROGRESS_PAUSE) { ty += AndroidUtilities.dpf2(8) * (1f - actionBar.getAlpha()); } - canvas.save(); - canvas.translate(tx, ty); - photoProgressViews[0].setScale(1.0f); - photoProgressViews[0].setAlpha(progressAlpha); - photoProgressViews[0].onDraw(canvas); + if (seekSpeedDrawable == null || !seekSpeedDrawable.isShown()) { + canvas.save(); + canvas.translate(tx, ty); + photoProgressViews[0].setScale(1.0f); + photoProgressViews[0].setAlpha(progressAlpha); + photoProgressViews[0].onDraw(canvas); - if (isActionBarVisible && allowShowFullscreenButton && fullscreenButton[0].getTag() == null) { - fullscreenButton[0].setAlpha(Math.min(fullscreenButton[0].getAlpha(), alpha)); - } + if (isActionBarVisible && allowShowFullscreenButton && fullscreenButton[0].getTag() == null) { + fullscreenButton[0].setAlpha(Math.min(fullscreenButton[0].getAlpha(), alpha)); + } - canvas.restore(); + canvas.restore(); + } } if (drawMiniProgress && !pipAnimationInProgress) { canvas.save(); @@ -18699,7 +18953,7 @@ private void onActionClick(boolean download) { } if (file == null || !file.exists()) { file = null; - if (SharedConfig.streamMedia && !DialogObject.isEncryptedDialog(currentMessageObject.getDialogId()) && currentMessageObject.isVideo() && currentMessageObject.canStreamVideo()) { + if (currentMessageObject.isVideo() && (currentMessageObject.hasVideoQualities() || SharedConfig.streamMedia) && !DialogObject.isEncryptedDialog(currentMessageObject.getDialogId()) && currentMessageObject.canStreamVideo()) { final int reference = FileLoader.getInstance(currentMessageObject.currentAccount).getFileReference(currentMessageObject); videoUrises = new ArrayList<>(); @@ -18865,23 +19119,28 @@ public void onLongPress(MotionEvent ev) { } public void onLongPress() { - if (videoPlayer != null && videoPlayerControlVisible && scale <= 1.1f) { + if (videoPlayer != null && scale <= 1.35f) { long current = videoPlayer.getCurrentPosition(); long total = videoPlayer.getDuration(); - if (current == C.TIME_UNSET || total < 15 * 1000) { + if (current == C.TIME_UNSET || total < 8 * 1000) { return; } float x = longPressX; int width = getContainerViewWidth(); - boolean forward; - if (x >= width / 3 * 2) { - forward = true; - } else if (x < width / 3) { - forward = false; + if (total > 180 * 1000) { + boolean forward; + if (x >= width / 3 * 2) { + forward = true; + } else if (x < width / 3) { + forward = false; + } else { + return; + } + longVideoPlayerRewinder.startRewind(videoPlayer, forward, currentVideoSpeed); } else { - return; + final boolean forward = x > width / 3; + videoPlayerRewinder.startRewind(videoPlayer, forward, longPressX, currentVideoSpeed, seekSpeedDrawable); } - videoPlayerRewinder.startRewind(videoPlayer, forward, currentVideoSpeed); } } @@ -18930,7 +19189,7 @@ public boolean onSingleTapConfirmed(MotionEvent e) { } closePhoto(true, false); if (currentMessageObject.sponsoredUrl != null) { - Browser.openUrl(LaunchActivity.instance != null ? LaunchActivity.instance : activityContext, Uri.parse(currentMessageObject.sponsoredUrl), true, false, false, null, null, false, MessagesController.getInstance(currentAccount).sponsoredLinksInappAllow); + Browser.openUrl(LaunchActivity.instance != null ? LaunchActivity.instance : activityContext, Uri.parse(currentMessageObject.sponsoredUrl), true, false, false, null, null, false, MessagesController.getInstance(currentAccount).sponsoredLinksInappAllow, false); } return true; } @@ -20999,7 +21258,7 @@ public void openAdsMenu() { if (parentFragment instanceof ChatActivity) { ((ChatActivity) parentFragment).logSponsoredClicked(currentMessageObject, false, true); } - Browser.openUrl(activityContext, Uri.parse(currentMessageObject.sponsoredUrl), true, false, false, null, null, false, MessagesController.getInstance(currentAccount).sponsoredLinksInappAllow); + Browser.openUrl(activityContext, Uri.parse(currentMessageObject.sponsoredUrl), true, false, false, null, null, false, MessagesController.getInstance(currentAccount).sponsoredLinksInappAllow, false); }); textView.setOnLongClickListener(e -> { if (currentMessageObject == null) { @@ -21076,13 +21335,10 @@ public void openAdsMenu() { } }); } - if (currentMessageObject.sponsoredCanReport || true) { + if (currentMessageObject.sponsoredCanReport) { o.add(R.drawable.msg_info, getString(R.string.AboutRevenueSharingAds), () -> { - RevenueSharingAdsInfoBottomSheet.showAlert(activityContext, parentFragment, resourcesProvider); + RevenueSharingAdsInfoBottomSheet.showAlert(activityContext, parentFragment, false, resourcesProvider); }); -// o.add(R.drawable.msg_block2, getString(R.string.ReportAd), () -> { -// -// }); if (parentFragment instanceof ChatActivity && !MessagesController.getInstance(account).premiumFeaturesBlocked()) { o.addGap(); o.add(R.drawable.msg_cancel, getString(R.string.RemoveAds), () -> { @@ -21158,8 +21414,32 @@ private void createAdButtonView() { } closePhoto(true, false); if (currentMessageObject.sponsoredUrl != null) { - Browser.openUrl(LaunchActivity.instance != null ? LaunchActivity.instance : activityContext, Uri.parse(currentMessageObject.sponsoredUrl), true, false, false, null, null, false, MessagesController.getInstance(currentAccount).sponsoredLinksInappAllow); + Browser.openUrl(LaunchActivity.instance != null ? LaunchActivity.instance : activityContext, Uri.parse(currentMessageObject.sponsoredUrl), true, false, false, null, null, false, MessagesController.getInstance(currentAccount).sponsoredLinksInappAllow, false); } }); } + + private void chooseSpeed(float speed, boolean isFinal, boolean closeMenu) { + if (speed != currentVideoSpeed) { + currentVideoSpeed = speed; + if (currentMessageObject != null) { + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("playback_speed", Activity.MODE_PRIVATE); + if (Math.abs(currentVideoSpeed - 1.0f) < 0.001f) { + preferences.edit().remove("speed" + currentMessageObject.getDialogId() + "_" + currentMessageObject.getId()).commit(); + } else { + preferences.edit().putFloat("speed" + currentMessageObject.getDialogId() + "_" + currentMessageObject.getId(), currentVideoSpeed).commit(); + } + } + if (videoPlayer != null) { + videoPlayer.setPlaybackSpeed(currentVideoSpeed); + } + if (photoViewerWebView != null) { + photoViewerWebView.setPlaybackSpeed(currentVideoSpeed); + } + } + setMenuItemIcon(true, isFinal); + if (closeMenu) { + videoItem.toggleSubMenu(); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PremiumFeatureCell.java b/TMessagesProj/src/main/java/org/telegram/ui/PremiumFeatureCell.java index c7abab3a774..a075330ebd2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PremiumFeatureCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PremiumFeatureCell.java @@ -1,6 +1,7 @@ package org.telegram.ui; import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.getMyLayerVersion; import android.content.Context; import android.graphics.Canvas; @@ -24,6 +25,8 @@ import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.AnimatedEmojiDrawable; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.UItem; +import org.telegram.ui.Stars.StarsIntroActivity; public class PremiumFeatureCell extends FrameLayout { @@ -149,4 +152,24 @@ protected void onDetachedFromWindow() { } super.onDetachedFromWindow(); } + + public static class Factory extends UItem.UItemFactory { + static { setup(new Factory()); } + + @Override + public PremiumFeatureCell createView(Context context, int currentAccount, int classGuid, Theme.ResourcesProvider resourcesProvider) { + return new PremiumFeatureCell(context, resourcesProvider); + } + + @Override + public void bindView(View view, UItem item, boolean divider) { + ((PremiumFeatureCell) view).setData((PremiumPreviewFragment.PremiumFeatureData) item.object, divider); + } + + public static UItem of(PremiumPreviewFragment.PremiumFeatureData data) { + UItem item = UItem.ofFactory(Factory.class); + item.object = data; + return item; + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PremiumPreviewFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/PremiumPreviewFragment.java index 6009d569f10..30bb7cfe3fe 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PremiumPreviewFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PremiumPreviewFragment.java @@ -876,13 +876,13 @@ public static void buyPremium(BaseFragment fragment) { public static void fillPremiumFeaturesList(ArrayList premiumFeatures, int currentAccount, boolean all) { MessagesController messagesController = MessagesController.getInstance(currentAccount); - premiumFeatures.add(new PremiumFeatureData(PREMIUM_FEATURE_LIMITS, R.drawable.msg_premium_limits, getString("PremiumPreviewLimits", R.string.PremiumPreviewLimits), LocaleController.formatString("PremiumPreviewLimitsDescription", R.string.PremiumPreviewLimitsDescription, + premiumFeatures.add(new PremiumFeatureData(PREMIUM_FEATURE_LIMITS, R.drawable.msg_premium_limits, getString(R.string.PremiumPreviewLimits), LocaleController.formatString(R.string.PremiumPreviewLimitsDescription, messagesController.channelsLimitPremium, messagesController.dialogFiltersLimitPremium, messagesController.dialogFiltersPinnedLimitPremium, messagesController.publicLinksLimitPremium, 4))); premiumFeatures.add(new PremiumFeatureData(PREMIUM_FEATURE_STORIES, R.drawable.msg_filled_stories, getString(R.string.PremiumPreviewStories), LocaleController.formatString(R.string.PremiumPreviewStoriesDescription))); - premiumFeatures.add(new PremiumFeatureData(PREMIUM_FEATURE_UPLOAD_LIMIT, R.drawable.msg_premium_uploads, getString("PremiumPreviewUploads", R.string.PremiumPreviewUploads), getString("PremiumPreviewUploadsDescription", R.string.PremiumPreviewUploadsDescription))); - premiumFeatures.add(new PremiumFeatureData(PREMIUM_FEATURE_DOWNLOAD_SPEED, R.drawable.msg_premium_speed, getString("PremiumPreviewDownloadSpeed", R.string.PremiumPreviewDownloadSpeed), getString("PremiumPreviewDownloadSpeedDescription", R.string.PremiumPreviewDownloadSpeedDescription))); - premiumFeatures.add(new PremiumFeatureData(PREMIUM_FEATURE_VOICE_TO_TEXT, R.drawable.msg_premium_voice, getString("PremiumPreviewVoiceToText", R.string.PremiumPreviewVoiceToText), getString("PremiumPreviewVoiceToTextDescription", R.string.PremiumPreviewVoiceToTextDescription))); - premiumFeatures.add(new PremiumFeatureData(PREMIUM_FEATURE_ADS, R.drawable.msg_premium_ads, getString("PremiumPreviewNoAds", R.string.PremiumPreviewNoAds), getString("PremiumPreviewNoAdsDescription", R.string.PremiumPreviewNoAdsDescription))); + premiumFeatures.add(new PremiumFeatureData(PREMIUM_FEATURE_UPLOAD_LIMIT, R.drawable.msg_premium_uploads, getString(R.string.PremiumPreviewUploads), getString(R.string.PremiumPreviewUploadsDescription))); + premiumFeatures.add(new PremiumFeatureData(PREMIUM_FEATURE_DOWNLOAD_SPEED, R.drawable.msg_premium_speed, getString(R.string.PremiumPreviewDownloadSpeed), getString(R.string.PremiumPreviewDownloadSpeedDescription))); + premiumFeatures.add(new PremiumFeatureData(PREMIUM_FEATURE_VOICE_TO_TEXT, R.drawable.msg_premium_voice, getString(R.string.PremiumPreviewVoiceToText), getString(R.string.PremiumPreviewVoiceToTextDescription))); + premiumFeatures.add(new PremiumFeatureData(PREMIUM_FEATURE_ADS, R.drawable.msg_premium_ads, getString(R.string.PremiumPreviewNoAds), getString(R.string.PremiumPreviewNoAdsDescription))); premiumFeatures.add(new PremiumFeatureData(PREMIUM_FEATURE_REACTIONS, R.drawable.msg_premium_reactions, getString(R.string.PremiumPreviewReactions2), getString(R.string.PremiumPreviewReactions2Description))); premiumFeatures.add(new PremiumFeatureData(PREMIUM_FEATURE_STICKERS, R.drawable.msg_premium_stickers, getString(R.string.PremiumPreviewStickers), getString(R.string.PremiumPreviewStickersDescription))); premiumFeatures.add(new PremiumFeatureData(PREMIUM_FEATURE_ANIMATED_EMOJI, R.drawable.msg_premium_emoji, getString(R.string.PremiumPreviewEmoji), getString(R.string.PremiumPreviewEmojiDescription))); @@ -951,9 +951,12 @@ public static void fillBusinessFeaturesList(ArrayList premiu } public static CharSequence applyNewSpan(String str) { + return applyNewSpan(str, -1); + } + public static CharSequence applyNewSpan(String str, int fontSize) { SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(str); spannableStringBuilder.append(" d"); - FilterCreateActivity.NewSpan span = new FilterCreateActivity.NewSpan(false); + FilterCreateActivity.NewSpan span = new FilterCreateActivity.NewSpan(false, fontSize); span.setColor(Theme.getColor(Theme.key_premiumGradient1)); spannableStringBuilder.setSpan(span, spannableStringBuilder.length() - 1, spannableStringBuilder.length(), 0); return spannableStringBuilder; @@ -1459,7 +1462,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi if (position == showAdsInfoRow) { privacyCell.setText(AndroidUtilities.replaceArrows(AndroidUtilities.replaceSingleTag(getString(R.string.ShowAdsInfo), () -> { - showDialog(new RevenueSharingAdsInfoBottomSheet(PremiumPreviewFragment.this, getContext(), getResourceProvider())); + showDialog(new RevenueSharingAdsInfoBottomSheet(getContext(), false, getResourceProvider(), null)); }), true)); } else if (position == statusRow && type == FEATURES_BUSINESS) { privacyCell.setText(getString(R.string.PremiumPreviewMoreBusinessFeaturesInfo)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PrivacyControlActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PrivacyControlActivity.java index f9b819e6249..74da5a9c962 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PrivacyControlActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PrivacyControlActivity.java @@ -99,9 +99,11 @@ public class PrivacyControlActivity extends BaseFragment implements Notification private ArrayList initialPlus = new ArrayList<>(); private ArrayList initialMinus = new ArrayList<>(); private final boolean[] initialPlusPremium = new boolean[2]; + private final boolean[] initialPlusMiniapps = new boolean[3]; private int rulesType; private final boolean[] currentPlusPremium = new boolean[2]; + private final boolean[] currentPlusMiniapps = new boolean[3]; private ArrayList currentPlus; private ArrayList currentMinus; @@ -151,6 +153,7 @@ public class PrivacyControlActivity extends BaseFragment implements Notification public final static int PRIVACY_RULES_TYPE_BIO = 9; public final static int PRIVACY_RULES_TYPE_MESSAGES = 10; public final static int PRIVACY_RULES_TYPE_BIRTHDAY = 11; + public final static int PRIVACY_RULES_TYPE_GIFTS = 12; public final static int TYPE_EVERYBODY = 0; public final static int TYPE_NOBODY = 1; @@ -478,6 +481,8 @@ public View createView(Context context) { actionBar.setTitle(LocaleController.getString(R.string.PrivacyMessages)); } else if (rulesType == PRIVACY_RULES_TYPE_BIRTHDAY) { actionBar.setTitle(LocaleController.getString(R.string.PrivacyBirthday)); + } else if (rulesType == PRIVACY_RULES_TYPE_GIFTS) { + actionBar.setTitle(LocaleController.getString(R.string.PrivacyGifts)); } actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override @@ -634,16 +639,29 @@ protected void dispatchDraw(Canvas canvas) { if (position == alwaysShareRow && rulesType == PRIVACY_RULES_TYPE_INVITE) { args.putBoolean("allowPremium", true); } + final boolean allowMiniapps; + if (rulesType == PRIVACY_RULES_TYPE_GIFTS) { + if (currentType == TYPE_NOBODY) { + allowMiniapps = (position == alwaysShareRow); + } else if (currentType == TYPE_CONTACTS) { + allowMiniapps = (position == alwaysShareRow); + } else if (currentType == TYPE_EVERYBODY) { + allowMiniapps = (position == neverShareRow); + } else allowMiniapps = false; + } else allowMiniapps = false; + args.putBoolean("allowMiniapps", allowMiniapps); GroupCreateActivity fragment = new GroupCreateActivity(args); - fragment.select(createFromArray, position == alwaysShareRow && currentPlusPremium[currentType == TYPE_CONTACTS ? 0 : 1]); - fragment.setDelegate((premium, ids) -> { + fragment.select(createFromArray, position == alwaysShareRow && currentPlusPremium[currentType == TYPE_CONTACTS ? 0 : 1], allowMiniapps && currentPlusMiniapps[currentType]); + fragment.setDelegate((premium, miniapps, ids) -> { if (position == neverShareRow) { currentMinus = ids; + currentPlusMiniapps[currentType] = allowMiniapps && miniapps; for (int a = 0; a < currentMinus.size(); a++) { currentPlus.remove(currentMinus.get(a)); } } else { currentPlusPremium[currentType == TYPE_CONTACTS ? 0 : 1] = premium; + currentPlusMiniapps[currentType] = allowMiniapps && miniapps; currentPlus = ids; for (int a = 0; a < currentPlus.size(); a++) { currentMinus.remove(currentPlus.get(a)); @@ -758,6 +776,8 @@ private void applyCurrentPrivacySettings() { req.key = new TLRPC.TL_inputPrivacyKeyVoiceMessages(); } else if (rulesType == PRIVACY_RULES_TYPE_BIRTHDAY) { req.key = new TLRPC.TL_inputPrivacyKeyBirthday(); + } else if (rulesType == PRIVACY_RULES_TYPE_GIFTS) { + req.key = new TLRPC.TL_inputPrivacyKeyStarGiftsAutoSave(); } else { req.key = new TLRPC.TL_inputPrivacyKeyStatusTimestamp(); } @@ -811,6 +831,13 @@ private void applyCurrentPrivacySettings() { if (currentType != 0 && currentPlusPremium[currentType == TYPE_CONTACTS ? 0 : 1]) { req.rules.add(new TLRPC.TL_inputPrivacyValueAllowPremium()); } + if (currentPlusMiniapps[currentType]) { + if (currentType == TYPE_EVERYBODY) { + req.rules.add(new TLRPC.TL_inputPrivacyValueDisallowBots()); + } else { + req.rules.add(new TLRPC.TL_inputPrivacyValueAllowBots()); + } + } AlertDialog progressDialog = null; if (getParentActivity() != null) { progressDialog = new AlertDialog(getParentActivity(), AlertDialog.ALERT_TYPE_SPINNER); @@ -873,6 +900,9 @@ private void checkPrivacy() { } currentPlusPremium[0] = initialPlusPremium[0] = rulesType == PRIVACY_RULES_TYPE_INVITE; currentPlusPremium[1] = initialPlusPremium[1] = false; + currentPlusMiniapps[TYPE_EVERYBODY] = initialPlusMiniapps[TYPE_EVERYBODY] = false; + currentPlusMiniapps[TYPE_NOBODY] = initialPlusMiniapps[TYPE_NOBODY] = rulesType == PRIVACY_RULES_TYPE_GIFTS; + currentPlusMiniapps[TYPE_CONTACTS] = initialPlusMiniapps[TYPE_CONTACTS] = false; currentPlus = new ArrayList<>(); currentMinus = new ArrayList<>(); ArrayList privacyRules = ContactsController.getInstance(currentAccount).getPrivacyRules(rulesType); @@ -881,6 +911,7 @@ private void checkPrivacy() { } else { int type = -1; boolean premium = false; + Boolean miniapps = null; boolean hadAllowContacts = false; for (int a = 0; a < privacyRules.size(); a++) { TLRPC.PrivacyRule rule = privacyRules.get(a); @@ -902,6 +933,10 @@ private void checkPrivacy() { currentMinus.addAll(privacyValueDisallowUsers.users); } else if (rule instanceof TLRPC.TL_privacyValueAllowPremium) { premium = true; + } else if (rule instanceof TLRPC.TL_privacyValueAllowBots) { + miniapps = true; + } else if (rule instanceof TLRPC.TL_privacyValueDisallowBots) { + miniapps = false; } else if (rule instanceof TLRPC.TL_privacyValueAllowAll) { type = 0; } else if (rule instanceof TLRPC.TL_privacyValueDisallowAll && !hadAllowContacts) { @@ -919,15 +954,16 @@ private void checkPrivacy() { } } } - if (type == TYPE_EVERYBODY || type == -1 && currentMinus.size() > 0) { + if (type == TYPE_EVERYBODY || type == -1 && (currentMinus.size() > 0 || miniapps != null && !miniapps)) { currentType = TYPE_EVERYBODY; } else if (type == TYPE_CONTACTS || type == -1 && currentMinus.size() > 0 && currentPlus.size() > 0) { currentType = TYPE_CONTACTS; - } else if (type == TYPE_NOBODY || type == -1 && currentPlus.size() > 0) { + } else if (type == TYPE_NOBODY || type == -1 && (currentPlus.size() > 0 || miniapps != null && miniapps)) { currentType = TYPE_NOBODY; } int a = currentType == TYPE_CONTACTS ? 0 : 1; currentPlusPremium[a] = initialPlusPremium[a] = premium; + currentPlusMiniapps[currentType] = initialPlusMiniapps[currentType] = miniapps != null; if (doneButton != null) { doneButton.setAlpha(0.0f); doneButton.setScaleX(0.0f); @@ -983,6 +1019,9 @@ private boolean hasChanges() { if (currentType != 0 && initialPlusPremium[currentType == TYPE_CONTACTS ? 0 : 1] != currentPlusPremium[currentType == TYPE_CONTACTS ? 0 : 1]) { return true; } + if (initialPlusMiniapps[currentType] != currentPlusMiniapps[currentType]) { + return true; + } if (initialMinus.size() != currentMinus.size()) { return true; } @@ -1052,7 +1091,8 @@ private void updateRows(boolean animated) { rulesType == PRIVACY_RULES_TYPE_PHONE || rulesType == PRIVACY_RULES_TYPE_VOICE_MESSAGES || rulesType == PRIVACY_RULES_TYPE_INVITE || - rulesType == PRIVACY_RULES_TYPE_BIRTHDAY + rulesType == PRIVACY_RULES_TYPE_BIRTHDAY || + rulesType == PRIVACY_RULES_TYPE_GIFTS ) { nobodyRow = rowCount++; } @@ -1371,6 +1411,13 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { value = LocaleController.formatString(R.string.PrivacyPremiumAnd, value); } } + if (currentPlusMiniapps[currentType] && currentType != TYPE_EVERYBODY) { + if (currentPlus == null || currentPlus.isEmpty()) { + value = LocaleController.formatString(R.string.PrivacyValueBots); + } else { + value = LocaleController.formatString(R.string.PrivacyValueBotsAnd, value); + } + } if (rulesType != PRIVACY_RULES_TYPE_LASTSEEN && rulesType != PRIVACY_RULES_TYPE_PHOTO && rulesType != PRIVACY_RULES_TYPE_BIO) { textCell.setTextAndValue(LocaleController.getString(R.string.AlwaysAllow), value, neverShareRow != -1); } else { @@ -1384,6 +1431,13 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else { value = LocaleController.getString(R.string.EmpryUsersPlaceholder); } + if (currentPlusMiniapps[currentType] && currentType == TYPE_EVERYBODY) { + if (currentMinus == null || currentMinus.isEmpty()) { + value = LocaleController.formatString(R.string.PrivacyValueBots); + } else { + value = LocaleController.formatString(R.string.PrivacyValueBotsAnd, value); + } + } if (rulesType != PRIVACY_RULES_TYPE_LASTSEEN && rulesType != PRIVACY_RULES_TYPE_PHOTO && rulesType != PRIVACY_RULES_TYPE_BIO) { textCell.setTextAndValue(LocaleController.getString(R.string.NeverAllow), value, false); } else { @@ -1505,6 +1559,8 @@ public void onClick(@NonNull View view) { privacyCell.setText(LocaleController.getString(R.string.PrivacyBioInfo3)); } else if (rulesType == PRIVACY_RULES_TYPE_BIRTHDAY) { privacyCell.setText(LocaleController.getString(R.string.PrivacyBirthdayInfo)); + } else if (rulesType == PRIVACY_RULES_TYPE_GIFTS) { + privacyCell.setText(LocaleController.getString(R.string.PrivacyGiftsInfo)); } else if (rulesType == PRIVACY_RULES_TYPE_P2P) { privacyCell.setText(LocaleController.getString(R.string.PrivacyCallsP2PHelp)); } else if (rulesType == PRIVACY_RULES_TYPE_CALLS) { @@ -1538,6 +1594,8 @@ public void onClick(@NonNull View view) { privacyCell.setText(LocaleController.getString(R.string.CustomCallInfo)); } else if (rulesType == PRIVACY_RULES_TYPE_INVITE) { privacyCell.setText(LocaleController.getString(R.string.CustomShareInfo)); + } else if (rulesType == PRIVACY_RULES_TYPE_GIFTS) { + privacyCell.setText(LocaleController.getString(R.string.CustomShareGiftsInfo)); } else if (rulesType == PRIVACY_RULES_TYPE_VOICE_MESSAGES) { privacyCell.setText(LocaleController.getString(R.string.PrivacyVoiceMessagesInfo2)); } else { @@ -1589,6 +1647,8 @@ public void onClick(@NonNull View view) { headerCell.setText(LocaleController.getString(R.string.PrivacyMessagesTitle)); } else if (rulesType == PRIVACY_RULES_TYPE_BIRTHDAY) { headerCell.setText(LocaleController.getString(R.string.PrivacyBirthdayTitle)); + } else if (rulesType == PRIVACY_RULES_TYPE_GIFTS) { + headerCell.setText(LocaleController.getString(R.string.PrivacyGiftsTitle)); } else { headerCell.setText(LocaleController.getString(R.string.LastSeenTitle)); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java index 8ec5c04f40d..1cb60841bd2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java @@ -89,6 +89,7 @@ public class PrivacySettingsActivity extends BaseFragment implements Notificatio private int lastSeenRow; private int profilePhotoRow; private int bioRow; + private int giftsRow; private int birthdayRow; private int forwardsRow; private int callsRow; @@ -242,7 +243,7 @@ public void onFragmentDestroy() { public View createView(Context context) { actionBar.setBackButtonImage(R.drawable.ic_ab_back); actionBar.setAllowOverlayTitle(true); - actionBar.setTitle(getString("PrivacySettings", R.string.PrivacySettings)); + actionBar.setTitle(getString(R.string.PrivacySettings)); actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override public void onItemClick(int id) { @@ -381,6 +382,8 @@ public boolean supportsPredictiveItemAnimations() { presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_BIO)); } else if (position == birthdayRow) { presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_BIRTHDAY)); + } else if (position == giftsRow) { + presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_GIFTS)); } else if (position == forwardsRow) { presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_FORWARDS)); } else if (position == voicesRow) { @@ -689,6 +692,7 @@ private void updateRows(boolean notify) { noncontactsRow = -1; } birthdayRow = rowCount++; + giftsRow = rowCount++; bioRow = rowCount++; groupsRow = rowCount++; privacyShadowRow = rowCount++; @@ -793,18 +797,19 @@ private void loadPasswordSettings() { } public static String formatRulesString(AccountInstance accountInstance, int rulesType) { - ArrayList privacyRules = accountInstance.getContactsController().getPrivacyRules(rulesType); + final ArrayList privacyRules = accountInstance.getContactsController().getPrivacyRules(rulesType); if (privacyRules == null || privacyRules.size() == 0) { - if (rulesType == 3) { - return getString("P2PNobody", R.string.P2PNobody); + if (rulesType == ContactsController.PRIVACY_RULES_TYPE_P2P) { + return getString(R.string.P2PNobody); } else { - return getString("LastSeenNobody", R.string.LastSeenNobody); + return getString(R.string.LastSeenNobody); } } int type = -1; int plus = 0; int minus = 0; boolean premium = false; + Boolean miniapps = null; for (int a = 0; a < privacyRules.size(); a++) { TLRPC.PrivacyRule rule = privacyRules.get(a); if (rule instanceof TLRPC.TL_privacyValueAllowChatParticipants) { @@ -833,6 +838,10 @@ public static String formatRulesString(AccountInstance accountInstance, int rule minus += privacyValueDisallowUsers.users.size(); } else if (rule instanceof TLRPC.TL_privacyValueAllowPremium) { premium = true; + } else if (rule instanceof TLRPC.TL_privacyValueAllowBots) { + miniapps = true; + } else if (rule instanceof TLRPC.TL_privacyValueDisallowBots) { + miniapps = false; } else if (type == -1) { if (rule instanceof TLRPC.TL_privacyValueAllowAll) { type = 0; @@ -844,17 +853,17 @@ public static String formatRulesString(AccountInstance accountInstance, int rule } } if (type == 0 || type == -1 && minus > 0) { - if (rulesType == 3) { + if (rulesType == ContactsController.PRIVACY_RULES_TYPE_P2P) { if (minus == 0) { - return getString("P2PEverybody", R.string.P2PEverybody); + return getString(R.string.P2PEverybody); } else { - return LocaleController.formatString("P2PEverybodyMinus", R.string.P2PEverybodyMinus, minus); + return LocaleController.formatString(R.string.P2PEverybodyMinus, minus); } } else { if (minus == 0) { - return getString("LastSeenEverybody", R.string.LastSeenEverybody); + return getString(miniapps != null && !miniapps ? R.string.PrivacyValueEveryoneExceptBots : R.string.LastSeenEverybody); } else { - return LocaleController.formatString("LastSeenEverybodyMinus", R.string.LastSeenEverybodyMinus, minus); + return LocaleController.formatString(miniapps != null && !miniapps ? R.string.PrivacyValueEveryoneExceptBotsMinus : R.string.LastSeenEverybodyMinus, minus); } } } else if (type == 2 || type == -1 && minus > 0 && plus > 0) { @@ -872,31 +881,46 @@ public static String formatRulesString(AccountInstance accountInstance, int rule } } else { if (plus == 0 && minus == 0) { - return getString(premium ? R.string.LastSeenContactsPremium : R.string.LastSeenContacts); + if (premium) { + return getString(R.string.LastSeenContactsPremium); + } else if (miniapps != null && miniapps) { + return LocaleController.getString(R.string.PrivacyContactsAndBotUsers); + } + return getString(R.string.LastSeenContacts); } else { if (plus != 0 && minus != 0) { - return LocaleController.formatString(premium ? R.string.LastSeenContactsPremiumMinusPlus : R.string.LastSeenContactsMinusPlus, minus, plus); + return LocaleController.formatString(miniapps != null && miniapps ? R.string.PrivacyContactsAndBotUsersMinusPlus : premium ? R.string.LastSeenContactsPremiumMinusPlus : R.string.LastSeenContactsMinusPlus, minus, plus); } else if (minus != 0) { - return LocaleController.formatString(premium ? R.string.LastSeenContactsPremiumMinus : R.string.LastSeenContactsMinus, minus); + return LocaleController.formatString(miniapps != null && miniapps ? R.string.PrivacyContactsAndBotUsersMinus :premium ? R.string.LastSeenContactsPremiumMinus : R.string.LastSeenContactsMinus, minus); } else { - return LocaleController.formatString(premium ? R.string.LastSeenContactsPremiumPlus : R.string.LastSeenContactsPlus, plus); + return LocaleController.formatString(miniapps != null && miniapps ? R.string.PrivacyContactsAndBotUsersPlus :premium ? R.string.LastSeenContactsPremiumPlus : R.string.LastSeenContactsPlus, plus); } } } } else if (type == 1 || plus > 0) { if (rulesType == PrivacyControlActivity.PRIVACY_RULES_TYPE_P2P) { if (plus == 0) { - return getString("P2PNobody", R.string.P2PNobody); + return getString(R.string.P2PNobody); } else { - return LocaleController.formatString("P2PNobodyPlus", R.string.P2PNobodyPlus, plus); + return LocaleController.formatString(R.string.P2PNobodyPlus, plus); } } else { if (plus == 0) { - return getString(premium ? R.string.LastSeenNobodyPremium : R.string.LastSeenNobody); + if (premium) { + return getString(R.string.LastSeenNobodyPremium); + } + if (miniapps != null && miniapps) { + return LocaleController.getString(R.string.PrivacyValueOnlyBots); + } + return getString(R.string.LastSeenNobody); } else { return LocaleController.formatString(premium ? R.string.LastSeenNobodyPremiumPlus : R.string.LastSeenNobodyPlus, plus); } } + } else if (miniapps != null) { + if (miniapps) { + return LocaleController.getString(R.string.PrivacyValueOnlyBots); + } } return "unknown"; } @@ -927,6 +951,7 @@ public boolean isEnabled(RecyclerView.ViewHolder holder) { position == profilePhotoRow && !getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_PHOTO) || position == bioRow && !getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_BIO) || position == birthdayRow && !getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_BIRTHDAY) || + position == giftsRow && !getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_GIFTS) || position == forwardsRow && !getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_FORWARDS) || position == phoneNumberRow && !getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_PHONE) || position == voicesRow && !getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_VOICE_MESSAGES) || @@ -984,6 +1009,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { boolean animated = holder.itemView.getTag() != null && ((Integer) holder.itemView.getTag()) == position; holder.itemView.setTag(position); TextSettingsCell textCell = (TextSettingsCell) holder.itemView; + textCell.setBetterLayout(true); if (position == webSessionsRow) { textCell.setText(getString("WebSessionsTitle", R.string.WebSessionsTitle), false); } else if (position == phoneNumberRow) { @@ -1042,6 +1068,14 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { value = formatRulesString(getAccountInstance(), ContactsController.PRIVACY_RULES_TYPE_BIRTHDAY); } textCell.setTextAndValue(getString(R.string.PrivacyBirthday), value, true); + } else if (position == giftsRow) { + if (getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_GIFTS)) { + showLoading = true; + loadingLen = 30; + } else { + value = formatRulesString(getAccountInstance(), ContactsController.PRIVACY_RULES_TYPE_GIFTS); + } + textCell.setTextAndValue(getString(R.string.PrivacyGifts), value, true); } else if (position == forwardsRow) { if (getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_FORWARDS)) { showLoading = true; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PrivacyUsersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PrivacyUsersActivity.java index 050c016ce57..35188631177 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PrivacyUsersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PrivacyUsersActivity.java @@ -207,9 +207,11 @@ public void onItemClick(int id) { } if (isAlwaysShare && rulesType == PrivacyControlActivity.PRIVACY_RULES_TYPE_INVITE) { args.putBoolean("allowPremium", true); + } else if (rulesType == PrivacyControlActivity.PRIVACY_RULES_TYPE_GIFTS) { + args.putBoolean("allowMiniapps", true); } GroupCreateActivity fragment = new GroupCreateActivity(args); - fragment.setDelegate((premium, ids) -> { + fragment.setDelegate((premium, miniapps, ids) -> { for (Long id1 : ids) { if (uidArray.contains(id1)) { continue; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java index 61096828731..c2236a1c6ac 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java @@ -270,7 +270,10 @@ import org.telegram.ui.Stories.recorder.DualCameraView; import org.telegram.ui.Stories.recorder.StoryRecorder; import org.telegram.ui.bots.BotBiometry; +import org.telegram.ui.bots.BotDownloads; +import org.telegram.ui.bots.BotLocation; import org.telegram.ui.bots.BotWebViewAttachedSheet; +import org.telegram.ui.bots.SetupEmojiStatusSheet; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -468,6 +471,8 @@ public void setAlpha(int a) { private Paint whitePaint = new Paint(); private boolean isBot; + private BotLocation botLocation; + private BotBiometry botBiometry; private TLRPC.ChatFull chatInfo; private TLRPC.UserFull userInfo; @@ -601,6 +606,15 @@ public void setAlpha(int a) { private int premiumGiftingRow; private int premiumSectionsRow; private int botAppRow; + private int botPermissionsHeader; + @Keep + private int botPermissionLocation; + @Keep + private int botPermissionEmojiStatus; + private int botPermissionEmojiStatusReqId; + @Keep + private int botPermissionBiometry; + private int botPermissionsDivider; private int settingsTimerRow; private int settingsKeyRow; @@ -614,7 +628,9 @@ public void setAlpha(int a) { private int subscribersRequestsRow; private int administratorsRow; private int settingsRow; - private int balanceRow; + private int botStarsBalanceRow; + private int botTonBalanceRow; + private int channelBalanceRow; private int balanceDividerRow; private int blockedUsersRow; private int membersSectionRow; @@ -1073,11 +1089,13 @@ protected void onDraw(Canvas canvas) { if (previousTransitionFragment != null) { ActionBar actionBar = previousTransitionFragment.getActionBar(); ActionBarMenu menu = actionBar.menu; - int restoreCount = canvas.save(); - canvas.translate(actionBar.getX() + menu.getX(), actionBar.getY() + menu.getY()); - canvas.saveLayerAlpha(0, 0, menu.getMeasuredWidth(), menu.getMeasuredHeight(), (int) (255 * (1f - avatarAnimationProgress)), Canvas.ALL_SAVE_FLAG); - menu.draw(canvas); - canvas.restoreToCount(restoreCount); + if (actionBar != null && menu != null) { + int restoreCount = canvas.save(); + canvas.translate(actionBar.getX() + menu.getX(), actionBar.getY() + menu.getY()); + canvas.saveLayerAlpha(0, 0, menu.getMeasuredWidth(), menu.getMeasuredHeight(), (int) (255 * (1f - avatarAnimationProgress)), Canvas.ALL_SAVE_FLAG); + menu.draw(canvas); + canvas.restoreToCount(restoreCount); + } } } if (y1 != v) { @@ -3975,15 +3993,15 @@ public void openExceptions() { presentFragment(fragment); } else if (position == settingsRow) { editItem.performClick(); - } else if (position == balanceRow) { + } else if (position == botStarsBalanceRow) { + presentFragment(new BotStarsActivity(BotStarsActivity.TYPE_STARS, userId)); + } else if (position == botTonBalanceRow) { + presentFragment(new BotStarsActivity(BotStarsActivity.TYPE_TON, userId)); + } else if (position == channelBalanceRow) { Bundle args = new Bundle(); - if (userInfo != null) { - presentFragment(new BotStarsActivity(userId)); - } else { - args.putLong("chat_id", chatId); - args.putBoolean("start_from_monetization", true); - presentFragment(new StatisticActivity(args)); - } + args.putLong("chat_id", chatId); + args.putBoolean("start_from_monetization", true); + presentFragment(new StatisticActivity(args)); } else if (position == blockedUsersRow) { Bundle args = new Bundle(); args.putLong("chat_id", chatId); @@ -4052,6 +4070,37 @@ public void openExceptions() { presentFragment(new PremiumPreviewFragment(PremiumPreviewFragment.FEATURES_BUSINESS, "settings")); } else if (position == premiumGiftingRow) { UserSelectorBottomSheet.open(0, BirthdayController.getInstance(currentAccount).getState()); + } else if (position == botPermissionLocation) { + if (botLocation != null) { + botLocation.setGranted(!botLocation.granted(), () -> { + ((TextCell) view).setChecked(botLocation.granted()); + }); + } + } else if (position == botPermissionBiometry) { + if (botBiometry != null) { + botBiometry.setGranted(!botBiometry.granted()); + ((TextCell) view).setChecked(botBiometry.granted()); + } + } else if (position == botPermissionEmojiStatus) { + ((TextCell) view).setChecked(!((TextCell) view).isChecked()); + if (botPermissionEmojiStatusReqId > 0) { + getConnectionsManager().cancelRequest(botPermissionEmojiStatusReqId, true); + } + TL_bots.toggleUserEmojiStatusPermission req = new TL_bots.toggleUserEmojiStatusPermission(); + req.bot = getMessagesController().getInputUser(userId); + req.enabled = ((TextCell) view).isChecked(); + if (userInfo != null) { + userInfo.bot_can_manage_emoji_status = req.enabled; + } + final int[] reqId = new int[1]; + reqId[0] = botPermissionEmojiStatusReqId = getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (!(res instanceof TLRPC.TL_boolTrue)) { + BulletinFactory.of(ProfileActivity.this).showForError(err); + } + if (botPermissionEmojiStatusReqId == reqId[0]) { + botPermissionEmojiStatusReqId = 0; + } + })); } else if (position == bizHoursRow) { hoursExpanded = !hoursExpanded; saveScrollPosition(); @@ -4125,7 +4174,7 @@ public boolean onItemClick(View view, int position) { !SharedConfig.payByInvoice ? "Enable Invoice Payment" : "Disable Invoice Payment", BuildVars.DEBUG_PRIVATE_VERSION ? "Update Attach Bots" : null, Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? (!SharedConfig.isUsingCamera2(currentAccount) ? "Use Camera 2 API" : "Use old Camera 1 API") : null, - BuildVars.DEBUG_VERSION ? "Clear bot biometry data" : null, + BuildVars.DEBUG_VERSION ? "Clear Mini Apps Permissions and Files" : null, BuildVars.DEBUG_PRIVATE_VERSION ? "Clear all login tokens" : null, SharedConfig.canBlurChat() && Build.VERSION.SDK_INT >= 31 ? (SharedConfig.useNewBlur ? "back to cpu blur" : "use new gpu blur") : null, SharedConfig.adaptableColorInBrowser ? "Disabled adaptive browser colors" : "Enable adaptive browser colors", @@ -4168,7 +4217,7 @@ public boolean onItemClick(View view, int position) { getMessagesStorage().clearSentMedia(); SharedConfig.setNoSoundHintShowed(false); SharedPreferences.Editor editor = MessagesController.getGlobalMainSettings().edit(); - editor.remove("archivehint").remove("proximityhint").remove("archivehint_l").remove("speedhint").remove("gifhint").remove("reminderhint").remove("soundHint").remove("themehint").remove("bganimationhint").remove("filterhint").remove("n_0").remove("storyprvhint").remove("storyhint").remove("storyhint2").remove("storydualhint").remove("storysvddualhint").remove("stories_camera").remove("dualcam").remove("dualmatrix").remove("dual_available").remove("archivehint").remove("askNotificationsAfter").remove("askNotificationsDuration").remove("viewoncehint").remove("taptostorysoundhint").remove("nothanos").remove("voiceoncehint").remove("savedhint").remove("savedsearchhint").remove("savedsearchtaghint").remove("groupEmojiPackHintShown").remove("newppsms").remove("monetizationadshint").apply(); + 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").remove("taptostorysoundhint").remove("nothanos").remove("voiceoncehint").remove("savedhint").remove("savedsearchhint").remove("savedsearchtaghint").remove("groupEmojiPackHintShown").remove("newppsms").remove("monetizationadshint").remove("seekSpeedHintShowed").remove("unsupport_video/av01").apply(); MessagesController.getEmojiSettings(currentAccount).edit().remove("featured_hidden").remove("emoji_featured_hidden").commit(); SharedConfig.textSelectionHintShows = 0; SharedConfig.lockRecordAudioVideoHint = 0; @@ -4402,6 +4451,9 @@ protected void onSend(LongSparseArray dids, int count, TLRPC.TL_fo SharedConfig.toggleUseCamera2(currentAccount); } else if (which == 29) { BotBiometry.clear(); + BotLocation.clear(); + BotDownloads.clear(); + SetupEmojiStatusSheet.clear(); } else if (which == 30) { AuthTokensHelper.clearLogInTokens(); } else if (which == 31) { @@ -7788,10 +7840,12 @@ public void didReceivedNotification(int id, int account, final Object... args) { avatarImage.setHasStories(needInsetForStories()); updateAvatarRoundRadius(); } - if (userInfo != null) { - storyView.setStories(userInfo.stories); - } else if (chatInfo != null) { - storyView.setStories(chatInfo.stories); + if (storyView != null) { + if (userInfo != null) { + storyView.setStories(userInfo.stories); + } else if (chatInfo != null) { + storyView.setStories(chatInfo.stories); + } } } else if (id == NotificationCenter.userIsPremiumBlockedUpadted) { if (otherItem != null) { @@ -8676,6 +8730,11 @@ private void updateRowsIds() { switchBackendRow = -1; versionRow = -1; botAppRow = -1; + botPermissionsHeader = -1; + botPermissionBiometry = -1; + botPermissionEmojiStatus = -1; + botPermissionLocation = -1; + botPermissionsDivider = -1; sendMessageRow = -1; reportRow = -1; @@ -8715,7 +8774,9 @@ private void updateRowsIds() { sharedMediaRow = -1; notificationsSimpleRow = -1; settingsRow = -1; - balanceRow = -1; + botStarsBalanceRow = -1; + botTonBalanceRow = -1; + channelBalanceRow = -1; balanceDividerRow = -1; unblockRow = -1; @@ -8879,6 +8940,28 @@ private void updateRowsIds() { infoEndRow = rowCount - 1; infoSectionRow = rowCount++; + if (isBot) { + if (botLocation == null && getContext() != null) botLocation = BotLocation.get(getContext(), currentAccount, userId); + if (botBiometry == null && getContext() != null) botBiometry = BotBiometry.get(getContext(), currentAccount, userId); + final boolean containsPermissionLocation = botLocation != null && botLocation.asked(); + final boolean containsPermissionBiometry = botBiometry != null && botBiometry.asked(); + final boolean containsPermissionEmojiStatus = userInfo != null && userInfo.bot_can_manage_emoji_status || SetupEmojiStatusSheet.getAccessRequested(getContext(), currentAccount, userId); + + if (containsPermissionEmojiStatus || containsPermissionLocation || containsPermissionBiometry) { + botPermissionsHeader = rowCount++; + if (containsPermissionEmojiStatus) { + botPermissionEmojiStatus = rowCount++; + } + if (containsPermissionLocation) { + botPermissionLocation = rowCount++; + } + if (containsPermissionBiometry) { + botPermissionBiometry = rowCount++; + } + botPermissionsDivider = rowCount++; + } + } + if (currentEncryptedChat instanceof TLRPC.TL_encryptedChat) { settingsTimerRow = rowCount++; settingsKeyRow = rowCount++; @@ -8895,15 +8978,18 @@ private void updateRowsIds() { boolean divider = false; if (user != null && user.bot) { - if (BotStarsController.getInstance(currentAccount).getBalance(userId) > 0 || BotStarsController.getInstance(currentAccount).hasTransactions(userId)) { - balanceRow = rowCount++; + if (userInfo != null && userInfo.can_view_revenue && BotStarsController.getInstance(currentAccount).getTONBalance(userId) > 0) { + botTonBalanceRow = rowCount++; + } + if (BotStarsController.getInstance(currentAccount).getBotStarsBalance(userId) > 0 || BotStarsController.getInstance(currentAccount).hasTransactions(userId)) { + botStarsBalanceRow = rowCount++; } } if (user != null && isBot && !user.bot_nochats) { addToGroupButtonRow = rowCount++; addToGroupInfoRow = rowCount++; - } else if (balanceRow >= 0) { + } else if (botStarsBalanceRow >= 0) { divider = true; } @@ -8960,28 +9046,28 @@ private void updateRowsIds() { infoSectionRow = rowCount++; if (ChatObject.isChannel(currentChat) && !currentChat.megagroup) { - if (chatInfo != null && (currentChat.creator || chatInfo.can_view_participants)) { + if (chatInfo != null && (currentChat.creator || chatInfo.can_view_participants) || BuildVars.DEBUG_PRIVATE_VERSION) { membersHeaderRow = rowCount++; subscribersRow = rowCount++; - if (chatInfo.requests_pending > 0) { + if (chatInfo != null && chatInfo.requests_pending > 0) { subscribersRequestsRow = rowCount++; } administratorsRow = rowCount++; - if (chatInfo.banned_count != 0 || chatInfo.kicked_count != 0) { + if (chatInfo != null && (chatInfo.banned_count != 0 || chatInfo.kicked_count != 0)) { blockedUsersRow = rowCount++; } long did = chatId != 0 ? -chatId : userId; if ( chatInfo != null && chatInfo.can_view_stars_revenue && ( - BotStarsController.getInstance(currentAccount).getBalance(did) > 0 || + BotStarsController.getInstance(currentAccount).getBotStarsBalance(did) > 0 || BotStarsController.getInstance(currentAccount).hasTransactions(did) ) || chatInfo != null && chatInfo.can_view_revenue && - BotStarsController.getInstance(currentAccount).getChannelBalance(did) > 0 + BotStarsController.getInstance(currentAccount).getTONBalance(did) > 0 ) { - balanceRow = rowCount++; + channelBalanceRow = rowCount++; } settingsRow = rowCount++; membersSectionRow = rowCount++; @@ -9339,8 +9425,8 @@ private void updateProfileData(boolean reload) { onlineTextView[a].setRightDrawableInside(true); onlineTextView[a].setRightDrawable(a == 1 && hiddenStatusButton ? getShowStatusButton() : null); onlineTextView[a].setRightDrawableOnClick(a == 1 && hiddenStatusButton ? v -> { - MessagePrivateSeenView.showSheet(getContext(), currentAccount, dialogId, true, null, () -> { - getMessagesController().reloadUser(dialogId); + MessagePrivateSeenView.showSheet(getContext(), currentAccount, getDialogId(), true, null, () -> { + getMessagesController().reloadUser(getDialogId()); }, resourcesProvider); } : null); Drawable leftIcon = currentEncryptedChat != null ? getLockIconDrawable() : null; @@ -9808,6 +9894,9 @@ private void updatedPeerColor() { if (sharedMediaLayout != null && sharedMediaLayout.scrollSlidingTextTabStrip != null) { sharedMediaLayout.scrollSlidingTextTabStrip.updateColors(); } + if (sharedMediaLayout != null && sharedMediaLayout.giftsContainer != null) { + sharedMediaLayout.giftsContainer.updateColors(); + } writeButtonSetBackground(); updateEmojiStatusDrawableColor(); } @@ -9945,7 +10034,8 @@ private void createActionBarMenu(boolean animated) { otherItem.addSubItem(delete_contact, R.drawable.msg_delete, LocaleController.getString(R.string.DeleteContact)); } if (!UserObject.isDeleted(user) && !isBot && currentEncryptedChat == null && !userBlocked && userId != 333000 && userId != 777000 && userId != 42777) { - if (!BuildVars.IS_BILLING_UNAVAILABLE && !user.self && !getMessagesController().premiumFeaturesBlocked()) { + if (!BuildVars.IS_BILLING_UNAVAILABLE && !user.self && !user.bot && !MessagesController.isSupportUser(user) && !getMessagesController().premiumPurchaseBlocked()) { + StarsController.getInstance(currentAccount).loadStarGifts(); otherItem.addSubItem(gift_premium, R.drawable.msg_gift_premium, LocaleController.getString(R.string.ProfileSendAGift)); } otherItem.addSubItem(start_secret_chat, R.drawable.msg_secret, LocaleController.getString(R.string.StartEncryptedChat)); @@ -11093,6 +11183,8 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { headerCell.setText(LocaleController.getString(R.string.SettingsHelp)); } else if (position == debugHeaderRow) { headerCell.setText(LocaleController.getString(R.string.SettingsDebug)); + } else if (position == botPermissionsHeader) { + headerCell.setText(LocaleController.getString(R.string.BotProfilePermissions)); } headerCell.setTextColor(applyPeerColor(getThemedColor(Theme.key_windowBackgroundWhiteBlueHeader), false)); break; @@ -11325,9 +11417,9 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else if (position == subscribersRow) { if (chatInfo != null) { if (ChatObject.isChannel(currentChat) && !currentChat.megagroup) { - textCell.setTextAndValueAndIcon(LocaleController.getString(R.string.ChannelSubscribers), String.format("%d", chatInfo.participants_count), R.drawable.msg_groups, position != membersSectionRow - 1); + textCell.setTextAndValueAndIcon(LocaleController.getString(R.string.ChannelSubscribers), LocaleController.formatNumber(chatInfo.participants_count, ','), R.drawable.msg_groups, position != membersSectionRow - 1); } else { - textCell.setTextAndValueAndIcon(LocaleController.getString(R.string.ChannelMembers), String.format("%d", chatInfo.participants_count), R.drawable.msg_groups, position != membersSectionRow - 1); + textCell.setTextAndValueAndIcon(LocaleController.getString(R.string.ChannelMembers), LocaleController.formatNumber(chatInfo.participants_count, ','), R.drawable.msg_groups, position != membersSectionRow - 1); } } else { if (ChatObject.isChannel(currentChat) && !currentChat.megagroup) { @@ -11348,10 +11440,9 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } } else if (position == settingsRow) { textCell.setTextAndIcon(LocaleController.getString(R.string.ChannelAdminSettings), R.drawable.msg_customize, position != membersSectionRow - 1); - } else if (position == balanceRow) { - long did = chatId != 0 ? -chatId : userId; - long stars_balance = BotStarsController.getInstance(currentAccount).getBalance(did); - long ton_balance = BotStarsController.getInstance(currentAccount).getChannelBalance(did); + } else if (position == channelBalanceRow) { + long stars_balance = BotStarsController.getInstance(currentAccount).getBotStarsBalance(-chatId); + long ton_balance = BotStarsController.getInstance(currentAccount).getTONBalance(-chatId); SpannableStringBuilder ssb = new SpannableStringBuilder(); if (ton_balance > 0) { if (ton_balance / 1_000_000_000.0 > 1000.0) { @@ -11370,7 +11461,31 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (ssb.length() > 0) ssb.append(" "); ssb.append("XTR ").append(AndroidUtilities.formatWholeNumber((int) stars_balance, 0)); } - textCell.setTextAndValueAndIcon(LocaleController.getString(R.string.ChannelStars), ChannelMonetizationLayout.replaceTON(StarsIntroActivity.replaceStarsWithPlain(ssb, .7f), textCell.getTextView().getPaint()), R.drawable.menu_feature_paid, true); + textCell.setTextAndValueAndIcon(getString(R.string.ChannelStars), ChannelMonetizationLayout.replaceTON(StarsIntroActivity.replaceStarsWithPlain(ssb, .7f), textCell.getTextView().getPaint()), R.drawable.menu_feature_paid, true); + } else if (position == botStarsBalanceRow) { + long stars_balance = BotStarsController.getInstance(currentAccount).getBotStarsBalance(userId); + SpannableStringBuilder ssb = new SpannableStringBuilder(); + if (stars_balance > 0) { + ssb.append("XTR ").append(AndroidUtilities.formatWholeNumber((int) stars_balance, 0)); + } + textCell.setTextAndValueAndIcon(getString(R.string.BotBalanceStars), ChannelMonetizationLayout.replaceTON(StarsIntroActivity.replaceStarsWithPlain(ssb, .7f), textCell.getTextView().getPaint()), R.drawable.menu_premium_main, true); + } else if (position == botTonBalanceRow) { + long ton_balance = BotStarsController.getInstance(currentAccount).getTONBalance(userId); + SpannableStringBuilder ssb = new SpannableStringBuilder(); + if (ton_balance > 0) { + if (ton_balance / 1_000_000_000.0 > 1000.0) { + ssb.append("TON ").append(AndroidUtilities.formatWholeNumber((int) (ton_balance / 1_000_000_000.0), 0)); + } else { + DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US); + symbols.setDecimalSeparator('.'); + DecimalFormat formatterTON = new DecimalFormat("#.##", symbols); + formatterTON.setMinimumFractionDigits(2); + formatterTON.setMaximumFractionDigits(3); + formatterTON.setGroupingUsed(false); + ssb.append("TON ").append(formatterTON.format(ton_balance / 1_000_000_000.0)); + } + } + textCell.setTextAndValueAndIcon(getString(R.string.BotBalanceTON), ChannelMonetizationLayout.replaceTON(StarsIntroActivity.replaceStarsWithPlain(ssb, .7f), textCell.getTextView().getPaint()), R.drawable.msg_ton, true); } else if (position == blockedUsersRow) { if (chatInfo != null) { textCell.setTextAndValueAndIcon(LocaleController.getString(R.string.ChannelBlacklist), String.format("%d", Math.max(chatInfo.banned_count, chatInfo.kicked_count)), R.drawable.msg_user_remove, position != membersSectionRow - 1); @@ -11457,6 +11572,12 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else if (position == premiumGiftingRow) { textCell.setTextAndIcon(LocaleController.getString(R.string.SendAGift), R.drawable.menu_gift, false); textCell.setImageLeft(23); + } else if (position == botPermissionLocation) { + textCell.setTextAndCheckAndColorfulIcon(LocaleController.getString(R.string.BotProfilePermissionLocation), botLocation != null && botLocation.granted(), R.drawable.filled_access_location, getThemedColor(Theme.key_color_green), botPermissionBiometry != -1); + } else if (position == botPermissionBiometry) { + textCell.setTextAndCheckAndColorfulIcon(LocaleController.getString(R.string.BotProfilePermissionBiometry), botBiometry != null && botBiometry.granted(), R.drawable.filled_access_fingerprint, getThemedColor(Theme.key_color_orange), false); + } else if (position == botPermissionEmojiStatus) { + textCell.setTextAndCheckAndColorfulIcon(LocaleController.getString(R.string.BotProfilePermissionEmojiStatus), userInfo != null && userInfo.bot_can_manage_emoji_status, R.drawable.filled_access_sleeping, getThemedColor(Theme.key_color_lightblue), botPermissionLocation != -1 || botPermissionBiometry != -1); } textCell.valueTextView.setTextColor(applyPeerColor(getThemedColor(Theme.key_windowBackgroundWhiteValueText), false)); break; @@ -11787,7 +11908,7 @@ public int getItemCount() { @Override public int getItemViewType(int position) { if (position == infoHeaderRow || position == membersHeaderRow || position == settingsSectionRow2 || - position == numberSectionRow || position == helpHeaderRow || position == debugHeaderRow) { + position == numberSectionRow || position == helpHeaderRow || position == debugHeaderRow || position == botPermissionsHeader) { return VIEW_TYPE_HEADER; } else if (position == phoneRow || position == locationRow || position == numberRow || position == birthdayRow) { return VIEW_TYPE_TEXT_DETAIL; @@ -11803,7 +11924,7 @@ public int getItemViewType(int position) { position == questionRow || position == devicesRow || position == filtersRow || position == stickersRow || position == faqRow || position == policyRow || position == sendLogsRow || position == sendLastLogsRow || position == clearLogsRow || position == switchBackendRow || position == setAvatarRow || position == addToGroupButtonRow || - position == addToContactsRow || position == liteModeRow || position == premiumGiftingRow || position == businessRow || position == balanceRow) { + position == addToContactsRow || position == liteModeRow || position == premiumGiftingRow || position == businessRow || position == botStarsBalanceRow || position == botTonBalanceRow || position == channelBalanceRow || position == botPermissionLocation || position == botPermissionBiometry || position == botPermissionEmojiStatus) { return VIEW_TYPE_TEXT; } else if (position == notificationsDividerRow) { return VIEW_TYPE_DIVIDER; @@ -11815,7 +11936,8 @@ public int getItemViewType(int position) { position == secretSettingsSectionRow || position == settingsSectionRow || position == devicesSectionRow || position == helpSectionCell || position == setAvatarSectionRow || position == passwordSuggestionSectionRow || position == phoneSuggestionSectionRow || position == premiumSectionsRow || position == reportDividerRow || - position == channelDividerRow || position == graceSuggestionSectionRow || position == balanceDividerRow + position == channelDividerRow || position == graceSuggestionSectionRow || position == balanceDividerRow || + position == botPermissionsDivider ) { return VIEW_TYPE_SHADOW; } else if (position >= membersStartRow && position < membersEndRow) { @@ -13135,9 +13257,16 @@ public void fillPositions(SparseIntArray sparseIntArray) { put(++pointer, bizLocationRow, sparseIntArray); put(++pointer, birthdayRow, sparseIntArray); put(++pointer, channelRow, sparseIntArray); - put(++pointer, balanceRow, sparseIntArray); + put(++pointer, botStarsBalanceRow, sparseIntArray); + put(++pointer, botTonBalanceRow, sparseIntArray); + put(++pointer, channelBalanceRow, sparseIntArray); put(++pointer, balanceDividerRow, sparseIntArray); put(++pointer, botAppRow, sparseIntArray); + put(++pointer, botPermissionsHeader, sparseIntArray); + put(++pointer, botPermissionLocation, sparseIntArray); + put(++pointer, botPermissionEmojiStatus, sparseIntArray); + put(++pointer, botPermissionBiometry, sparseIntArray); + put(++pointer, botPermissionsDivider, sparseIntArray); } private void put(int id, int position, SparseIntArray sparseIntArray) { @@ -13538,7 +13667,7 @@ protected boolean disablePermissionCheck() { fragment.setResourceProvider(resourcesProvider); TLRPC.TL_message message = new TLRPC.TL_message(); message.local_id = -1; - message.peer_id = getMessagesController().getPeer(dialogId); + message.peer_id = getMessagesController().getPeer(getDialogId()); TLRPC.TL_messageMediaGeo media = new TLRPC.TL_messageMediaGeo(); media.geo = userInfo.business_location.geo_point; media.address = userInfo.business_location.address; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ReportBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/ReportBottomSheet.java index 872a9c6d5d5..b47376c162c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ReportBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ReportBottomSheet.java @@ -254,8 +254,8 @@ protected boolean canDismissWithSwipe() { private void submitOption(final CharSequence optionText, final byte[] option, final String comment) { TLObject request; if (sponsored) { - TLRPC.TL_channels_reportSponsoredMessage req = new TLRPC.TL_channels_reportSponsoredMessage(); - req.channel = MessagesController.getInstance(currentAccount).getInputChannel(-dialogId); + TLRPC.TL_messages_reportSponsoredMessage req = new TLRPC.TL_messages_reportSponsoredMessage(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(dialogId); req.random_id = sponsoredId; req.option = option; request = req; @@ -937,8 +937,8 @@ public static void openSponsored( final long dialogId = fragment.getDialogId(); if (context == null) return; - TLRPC.TL_channels_reportSponsoredMessage req = new TLRPC.TL_channels_reportSponsoredMessage(); - req.channel = MessagesController.getInstance(currentAccount).getInputChannel(-dialogId); + TLRPC.TL_messages_reportSponsoredMessage req = new TLRPC.TL_messages_reportSponsoredMessage(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(dialogId); final byte[] sponsoredId = req.random_id = message.sponsoredId; req.option = new byte[]{}; ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/RevenueSharingAdsInfoBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/RevenueSharingAdsInfoBottomSheet.java index a2730606063..f8bcb14673a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/RevenueSharingAdsInfoBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/RevenueSharingAdsInfoBottomSheet.java @@ -24,44 +24,81 @@ import android.widget.ScrollView; import android.widget.TextView; +import androidx.core.content.ContextCompat; + 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.messenger.browser.Browser; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.BottomSheetWithRecyclerListView; import org.telegram.ui.Components.ColoredImageSpan; +import org.telegram.ui.Components.ItemOptions; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LinkSpanDrawable; import org.telegram.ui.Components.RLottieImageView; +import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.UItem; +import org.telegram.ui.Components.UniversalAdapter; + +import java.util.ArrayList; + +public class RevenueSharingAdsInfoBottomSheet extends BottomSheetWithRecyclerListView { -public class RevenueSharingAdsInfoBottomSheet extends BottomSheet { private static final int ITEM_HORIZONTAL_PADDING = 27; private static final int ICON_SIZE = 24; private static final int ITEM_TEXT_PADDING = 68; private final Paint topIconBgPaint; + private final LinearLayout customView; @SuppressLint("UseCompatLoadingForDrawables") - public RevenueSharingAdsInfoBottomSheet(BaseFragment baseFragment, Context context, Theme.ResourcesProvider resourcesProvider) { - super(context, false, resourcesProvider); + public RevenueSharingAdsInfoBottomSheet(Context context, boolean bot, Theme.ResourcesProvider resourcesProvider, Utilities.Callback options) { + super(context, null, false, false, false, resourcesProvider); fixNavigationBar(); + topPadding = .2f; + topIconBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); topIconBgPaint.setStyle(Paint.Style.FILL); topIconBgPaint.setColor(Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider)); - LinearLayout linearLayout = new LinearLayout(context); + LinearLayout linearLayout = customView = new LinearLayout(context); + linearLayout.setPadding(backgroundPaddingLeft + dp(6), 0, backgroundPaddingLeft + dp(6), 0); linearLayout.setOrientation(LinearLayout.VERTICAL); + FrameLayout topView = new FrameLayout(context); RLottieImageView imageView = new RLottieImageView(getContext()); imageView.setScaleType(ImageView.ScaleType.CENTER); imageView.setImageResource(R.drawable.large_ads_info); imageView.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); imageView.setBackground(Theme.createCircleDrawable(dp(80), Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider))); - linearLayout.addView(imageView, LayoutHelper.createLinear(80, 80, Gravity.CENTER_HORIZONTAL, 0, 28, 0, 0)); + topView.addView(imageView, LayoutHelper.createFrame(80, 80, Gravity.CENTER_HORIZONTAL, 0, 20, 0, 0)); + + if (options != null) { + ImageView optionsView = new ImageView(context); + optionsView.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_ab_other)); + optionsView.setContentDescription(LocaleController.getString(R.string.AccDescrMoreOptions)); + optionsView.setScaleType(ImageView.ScaleType.CENTER); + optionsView.setColorFilter(Theme.getColor(Theme.key_dialogTextGray3)); + optionsView.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector), 1)); + optionsView.setOnClickListener(v -> { + options.run( + ItemOptions.makeOptions(container, resourcesProvider, optionsView, true) + .setGravity(Gravity.RIGHT) + .setDrawScrim(false) + .translate(dp(12), dp(-32)) + ); + }); + topView.addView(optionsView, LayoutHelper.createFrame(24, 24, Gravity.RIGHT | Gravity.TOP, 12, 14, 14, 12)); + } + + linearLayout.addView(topView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 100, 0, 0, 0, 0)); TextView topTitle = new TextView(context); topTitle.setText(LocaleController.getString(R.string.AboutRevenueSharingAds)); @@ -78,16 +115,18 @@ public RevenueSharingAdsInfoBottomSheet(BaseFragment baseFragment, Context conte topSubtitle.setGravity(Gravity.CENTER_HORIZONTAL); linearLayout.addView(topSubtitle, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 22, 8, 22, 0)); - FrameLayout info1 = new FeatureCell(context, R.drawable.menu_privacy, LocaleController.getString(R.string.RevenueSharingAdsInfo1Title), LocaleController.getString(R.string.RevenueSharingAdsInfo1Subtitle)); + FrameLayout info1 = new FeatureCell(context, R.drawable.menu_privacy, LocaleController.getString(bot ? R.string.RevenueSharingAdsInfo1TitleBot : R.string.RevenueSharingAdsInfo1Title), LocaleController.getString(bot ? R.string.RevenueSharingAdsInfo1SubtitleBot : R.string.RevenueSharingAdsInfo1Subtitle)); linearLayout.addView(info1, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, 20, 0, 0)); - FrameLayout info2 = new FeatureCell(context, R.drawable.menu_feature_split, LocaleController.getString(R.string.RevenueSharingAdsInfo2Title), LocaleController.getString(R.string.RevenueSharingAdsInfo2Subtitle)); + FrameLayout info2 = new FeatureCell(context, R.drawable.menu_feature_split, LocaleController.getString(bot ? R.string.RevenueSharingAdsInfo2TitleBot : R.string.RevenueSharingAdsInfo2Title), LocaleController.getString(bot ? R.string.RevenueSharingAdsInfo2SubtitleBot : R.string.RevenueSharingAdsInfo2Subtitle)); linearLayout.addView(info2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, 16, 0, 0)); - String info3DescriptionString = LocaleController.formatString("RevenueSharingAdsInfo3Subtitle", R.string.RevenueSharingAdsInfo3Subtitle, MessagesController.getInstance(baseFragment.getCurrentAccount()).channelRestrictSponsoredLevelMin); + String info3DescriptionString = LocaleController.formatString(bot ? R.string.RevenueSharingAdsInfo3SubtitleBot : R.string.RevenueSharingAdsInfo3Subtitle, MessagesController.getInstance(UserConfig.selectedAccount).channelRestrictSponsoredLevelMin); SpannableStringBuilder info3Description = AndroidUtilities.replaceSingleTag(info3DescriptionString, Theme.key_chat_messageLinkIn, 0, () -> { + BaseFragment lastFragment = LaunchActivity.getSafeLastFragment(); + if (lastFragment == null) return; BaseFragment premiumFragment = new PremiumPreviewFragment(PremiumPreviewFragment.featureTypeToServerString(PremiumPreviewFragment.PREMIUM_FEATURE_ADS)); - baseFragment.presentFragment(premiumFragment); + lastFragment.presentFragment(premiumFragment); dismiss(); }); @@ -101,16 +140,20 @@ public RevenueSharingAdsInfoBottomSheet(BaseFragment baseFragment, Context conte linearLayout.addView(divider, dividerLayoutParams); TextView textViewDescription4 = new TextView(context); - textViewDescription4.setText(LocaleController.getString(R.string.RevenueSharingAdsInfo4Title)); + textViewDescription4.setText(LocaleController.getString(bot ? R.string.RevenueSharingAdsInfo4TitleBot : R.string.RevenueSharingAdsInfo4Title)); textViewDescription4.setTypeface(AndroidUtilities.bold()); textViewDescription4.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); textViewDescription4.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); textViewDescription4.setTextAlignment(TextView.TEXT_ALIGNMENT_CENTER); + textViewDescription4.setGravity(Gravity.CENTER); linearLayout.addView(textViewDescription4, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 22, 0, 22, 0)); - SpannableStringBuilder bottomSubtitle1 = AndroidUtilities.replaceTags(LocaleController.getString(R.string.RevenueSharingAdsInfo4Subtitle2)); + SpannableStringBuilder bottomSubtitle1 = AndroidUtilities.replaceTags(LocaleController.getString(bot ? R.string.RevenueSharingAdsInfo4Subtitle2Bot : R.string.RevenueSharingAdsInfo4Subtitle2)); String bottomSubtitle2 = getString(R.string.RevenueSharingAdsInfo4SubtitleLearnMore); - SpannableStringBuilder stringBuilder2 = AndroidUtilities.replaceSingleTag(bottomSubtitle2, Theme.key_chat_messageLinkIn, 0, () -> Browser.openUrl(getContext(), LocaleController.getString(R.string.PromoteUrl))); + SpannableStringBuilder stringBuilder2 = AndroidUtilities.replaceSingleTag(bottomSubtitle2, Theme.key_chat_messageLinkIn, 0, () -> { + dismiss(); + Browser.openUrl(getContext(), LocaleController.getString(R.string.PromoteUrl)); + }); SpannableString arrowStr = new SpannableString(">"); ColoredImageSpan span = new ColoredImageSpan(R.drawable.attach_arrow_right); span.setOverrideColor(Theme.getColor(Theme.key_chat_messageLinkIn)); @@ -140,13 +183,19 @@ public RevenueSharingAdsInfoBottomSheet(BaseFragment baseFragment, Context conte buttonTextView.setOnClickListener(e -> dismiss()); linearLayout.addView(buttonTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, 0, 14, 22, 14, 14)); - ScrollView scrollView = new ScrollView(getContext()); - scrollView.addView(linearLayout); - setCustomView(scrollView); +// ScrollView scrollView = new ScrollView(getContext()); +// scrollView.addView(linearLayout); +// setCustomView(scrollView); + + adapter.update(false); } - public static RevenueSharingAdsInfoBottomSheet showAlert(Context context, BaseFragment fragment, Theme.ResourcesProvider resourcesProvider) { - RevenueSharingAdsInfoBottomSheet alert = new RevenueSharingAdsInfoBottomSheet(fragment, context, resourcesProvider); + public static RevenueSharingAdsInfoBottomSheet showAlert(Context context, BaseFragment fragment, boolean bot, Theme.ResourcesProvider resourcesProvider) { + return showAlert(context, fragment, bot, resourcesProvider, null); + } + + public static RevenueSharingAdsInfoBottomSheet showAlert(Context context, BaseFragment fragment, boolean bot, Theme.ResourcesProvider resourcesProvider, Utilities.Callback options) { + RevenueSharingAdsInfoBottomSheet alert = new RevenueSharingAdsInfoBottomSheet(context, bot, resourcesProvider, options); if (fragment != null) { if (fragment.getParentActivity() != null) { fragment.showDialog(alert); @@ -183,4 +232,21 @@ public FeatureCell(Context context, int icon, CharSequence header, CharSequence addView(tvSubtitle, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, isRtl ? Gravity.RIGHT : Gravity.LEFT, isRtl ? ITEM_HORIZONTAL_PADDING : ITEM_TEXT_PADDING, 18, isRtl ? ITEM_TEXT_PADDING : ITEM_HORIZONTAL_PADDING, 0)); } } + + @Override + protected CharSequence getTitle() { + return LocaleController.getString(R.string.AboutRevenueSharingAds); + } + + private UniversalAdapter adapter; + + @Override + protected RecyclerListView.SelectionAdapter createAdapter(RecyclerListView listView) { + return adapter = new UniversalAdapter(listView, getContext(), currentAccount, 0, true, this::fillItems, resourcesProvider); + } + + public void fillItems(ArrayList items, UniversalAdapter adapter) { + items.add(UItem.asCustom(customView)); + } + } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SelectAnimatedEmojiDialog.java b/TMessagesProj/src/main/java/org/telegram/ui/SelectAnimatedEmojiDialog.java index 800fa64290c..6f105d3edbf 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SelectAnimatedEmojiDialog.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SelectAnimatedEmojiDialog.java @@ -668,7 +668,7 @@ protected boolean onTabClick(int index) { if (index > 0 && sectionToPosition.indexOfKey(index - 1) >= 0) { position = sectionToPosition.get(index - 1); } - scrollToPosition(position, AndroidUtilities.dp(-2)); + scrollToPosition(position, AndroidUtilities.dp(-2 + (type == TYPE_CHAT_REACTIONS ? 7 : 0))); SelectAnimatedEmojiDialog.this.emojiTabs.select(index); emojiGridView.scrolledByUserOnce = true; search(null); @@ -762,7 +762,7 @@ protected float animateByScale(View view) { emojiItemAnimator.setMoveInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); emojiItemAnimator.setDelayAnimations(false); emojiGridView.setItemAnimator(emojiItemAnimator); - emojiGridView.setPadding(dp(5), dp(type == TYPE_CHAT_REACTIONS ? 8 : 2), dp(5), dp(2 + 36)); + emojiGridView.setPadding(dp(5), dp(2), dp(5), dp(2 + 36)); adapter = new Adapter(); emojiGridView.setAdapter(adapter); @@ -2574,6 +2574,10 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi int index = positionToSection.get(position); if (index >= 0) { EmojiView.EmojiPack pack = packs.get(index); + if (pack.needLoadSet != null) { + MediaDataController.getInstance(currentAccount).getStickerSet(pack.needLoadSet, false); + pack.needLoadSet = null; + } header.setText(pack.set.title, !pack.free && !UserConfig.getInstance(currentAccount).isPremium() && type != TYPE_AVATAR_CONSTRUCTOR && type != TYPE_SET_REPLY_ICON && type != TYPE_SET_REPLY_ICON_BOTTOM && type != TYPE_CHAT_REACTIONS); } else { header.setText(null, false); @@ -3574,7 +3578,7 @@ private void updateRows(boolean updateEmojipacks, boolean animated, boolean diff recentStickers.clear(); standardEmojis.clear(); - if ((!installedEmojipacks.isEmpty() || type == TYPE_AVATAR_CONSTRUCTOR) && type != TYPE_SET_REPLY_ICON && type != TYPE_SET_REPLY_ICON_BOTTOM && type != TYPE_CHAT_REACTIONS && type != TYPE_EXPANDABLE_REACTIONS) { + if ((!installedEmojipacks.isEmpty() || type == TYPE_AVATAR_CONSTRUCTOR) && type != TYPE_SET_REPLY_ICON && type != TYPE_SET_REPLY_ICON_BOTTOM && type != TYPE_EXPANDABLE_REACTIONS) { searchRow = totalCount++; rowHashCodes.add(9L); } else { @@ -3899,12 +3903,17 @@ private void updateRows(boolean updateEmojipacks, boolean animated, boolean diff continue; } + TLRPC.InputStickerSet needLoadSet = null; ArrayList documents = null; if (set1 instanceof TLRPC.TL_stickerSetNoCovered) { TLRPC.TL_messages_stickerSet fullSet = mediaDataController.getStickerSet(MediaDataController.getInputStickerSet(set1.set), set1.set.hash, true); if (fullSet != null) { documents = fullSet.documents; isPremiumPack = MessageObject.isPremiumEmojiPack(fullSet); + } else { + needLoadSet = MediaDataController.getInputStickerSet(set1.set); + documents = new ArrayList<>(); + isPremiumPack = true; } } else if (set1 instanceof TLRPC.TL_stickerSetFullCovered) { documents = ((TLRPC.TL_stickerSetFullCovered) set1).documents; @@ -3928,6 +3937,7 @@ private void updateRows(boolean updateEmojipacks, boolean animated, boolean diff rowHashCodes.add(9211 + 13L * set.id); EmojiView.EmojiPack pack = new EmojiView.EmojiPack(); + pack.needLoadSet = needLoadSet; pack.installed = installedEmojiSets.contains(set.id); pack.featured = true; pack.free = !isPremiumPack; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SpeedButtonsLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/SpeedButtonsLayout.java new file mode 100644 index 00000000000..1f29a1a8ec6 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/SpeedButtonsLayout.java @@ -0,0 +1,109 @@ +package org.telegram.ui; + +import android.content.Context; +import android.view.Gravity; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.LinearLayout; + +import androidx.core.graphics.ColorUtils; +import androidx.core.math.MathUtils; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MediaController; +import org.telegram.messenger.R; +import org.telegram.ui.ActionBar.ActionBarMenuItem; +import org.telegram.ui.ActionBar.ActionBarMenuSlider; +import org.telegram.ui.ActionBar.ActionBarMenuSubItem; +import org.telegram.ui.ActionBar.ActionBarPopupWindow; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.PopupSwipeBackLayout; +import org.telegram.ui.Components.SpeedIconDrawable; + +public class SpeedButtonsLayout extends LinearLayout { + + ActionBarMenuSubItem[] speedItems = new ActionBarMenuSubItem[5]; + public SpeedButtonsLayout(Context context, Callback callback) { + super(context); + setOrientation(VERTICAL); + + ActionBarMenuSubItem item = ActionBarMenuItem.addItem(this, R.drawable.msg_speed_0_2, LocaleController.getString(R.string.SpeedVerySlow), false, null); + item.setColors(0xfffafafa, 0xfffafafa); + item.setOnClickListener((view) -> { + callback.onSpeedSelected(0.2f, true, true); + }); + item.setSelectorColor(0x0fffffff); + speedItems[0] = item; + + item = ActionBarMenuItem.addItem(this, R.drawable.msg_speed_slow, LocaleController.getString(R.string.SpeedSlow), false, null); + item.setColors(0xfffafafa, 0xfffafafa); + item.setOnClickListener((view) -> { + callback.onSpeedSelected(0.5f, true, true); + }); + item.setSelectorColor(0x0fffffff); + speedItems[1] = item; + + item = ActionBarMenuItem.addItem(this, R.drawable.msg_speed_normal, LocaleController.getString(R.string.SpeedNormal), false, null); + item.setColors(0xfffafafa, 0xfffafafa); + item.setOnClickListener((view) -> { + callback.onSpeedSelected(1f, true, true); + }); + item.setSelectorColor(0x0fffffff); + speedItems[2] = item; + + item = ActionBarMenuItem.addItem(this, R.drawable.msg_speed_fast, LocaleController.getString(R.string.SpeedFast), false, null); + item.setColors(0xfffafafa, 0xfffafafa); + item.setOnClickListener((view) -> { + callback.onSpeedSelected(1.5f, true, true); + }); + item.setSelectorColor(0x0fffffff); + speedItems[3] = item; + + item = ActionBarMenuItem.addItem(this, R.drawable.msg_speed_superfast, LocaleController.getString(R.string.SpeedVeryFast), false, null); + item.setColors(0xfffafafa, 0xfffafafa); + item.setOnClickListener((view) -> { + callback.onSpeedSelected(2f, true, true); + }); + item.setSelectorColor(0x0fffffff); + speedItems[4] = item; + + FrameLayout gap = new FrameLayout(context) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + }; + gap.setMinimumWidth(AndroidUtilities.dp(196)); + gap.setBackgroundColor(0xff181818); + addView(gap); + LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) gap.getLayoutParams(); + if (LocaleController.isRTL) { + layoutParams.gravity = Gravity.RIGHT; + } + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = AndroidUtilities.dp(8); + gap.setLayoutParams(layoutParams); + } + + public void update(float currentVideoSpeed, boolean isFinal) { + for (int a = 0; a < speedItems.length; a++) { + if (isFinal && ( + a == 0 && Math.abs(currentVideoSpeed - 0.2f) < 0.01f || + a == 1 && Math.abs(currentVideoSpeed - 0.5f) < 0.1f || + a == 2 && Math.abs(currentVideoSpeed - 1.0f) < 0.1f || + a == 3 && Math.abs(currentVideoSpeed - 1.5f) < 0.1f || + a == 4 && Math.abs(currentVideoSpeed - 2.0f) < 0.1f + )) { + speedItems[a].setColors(0xff6BB6F9, 0xff6BB6F9); + } else { + speedItems[a].setColors(0xfffafafa, 0xfffafafa); + } + } + } + + public interface Callback { + void onSpeedSelected(float speed, boolean isFinal, boolean closeMenu); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stars/BotStarsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/Stars/BotStarsActivity.java index bf5ea1ba38a..a354c3905a4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stars/BotStarsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stars/BotStarsActivity.java @@ -1,7 +1,10 @@ package org.telegram.ui.Stars; +import static org.telegram.messenger.AndroidUtilities.REPLACING_TAG_TYPE_LINK_NBSP; import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.LocaleController.formatString; import static org.telegram.messenger.LocaleController.getString; +import static org.telegram.ui.ChannelMonetizationLayout.replaceTON; import android.app.Activity; import android.content.Context; @@ -26,6 +29,7 @@ import android.widget.Space; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.core.view.NestedScrollingParent3; import androidx.core.view.NestedScrollingParentHelper; import androidx.core.view.ViewCompat; @@ -43,8 +47,10 @@ import org.telegram.messenger.UserObject; import org.telegram.messenger.browser.Browser; import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.tl.TL_stars; +import org.telegram.tgnet.tl.TL_stats; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.BackDrawable; @@ -57,6 +63,7 @@ import org.telegram.ui.Components.ChatAvatarContainer; import org.telegram.ui.Components.ColoredImageSpan; import org.telegram.ui.Components.EditTextBoldCursor; +import org.telegram.ui.Components.FlickerLoadingView; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.OutlineTextContainerView; import org.telegram.ui.Components.RecyclerListView; @@ -69,11 +76,17 @@ import org.telegram.ui.TwoStepVerificationActivity; import org.telegram.ui.TwoStepVerificationSetupActivity; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.util.ArrayList; import java.util.Locale; public class BotStarsActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { + public static final int TYPE_STARS = 0; + public static final int TYPE_TON = 1; + + public final int type; public final long bot_id; private ChatAvatarContainer avatarContainer; @@ -86,6 +99,11 @@ public class BotStarsActivity extends BaseFragment implements NotificationCenter private final ChannelMonetizationLayout.ProceedOverview availableValue = ChannelMonetizationLayout.ProceedOverview.as("XTR", getString(R.string.BotStarsOverviewAvailableBalance)); private final ChannelMonetizationLayout.ProceedOverview totalValue = ChannelMonetizationLayout.ProceedOverview.as("XTR", getString(R.string.BotStarsOverviewTotalBalance)); private final ChannelMonetizationLayout.ProceedOverview totalProceedsValue = ChannelMonetizationLayout.ProceedOverview.as("XTR", getString(R.string.BotStarsOverviewTotalProceeds)); + + private final ChannelMonetizationLayout.ProceedOverview tonAvailableValue = ChannelMonetizationLayout.ProceedOverview.as("TON", getString(R.string.BotMonetizationOverviewAvailable)); + private final ChannelMonetizationLayout.ProceedOverview tonLastWithdrawalValue = ChannelMonetizationLayout.ProceedOverview.as("TON", getString(R.string.BotMonetizationOverviewLastWithdrawal)); + private final ChannelMonetizationLayout.ProceedOverview tonLifetimeValue = ChannelMonetizationLayout.ProceedOverview.as("TON", getString(R.string.BotMonetizationOverviewTotal)); + private final CharSequence withdrawInfo; private StarsIntroActivity.StarsTransactionsLayout transactionsLayout; @@ -105,13 +123,24 @@ public class BotStarsActivity extends BaseFragment implements NotificationCenter private ColoredImageSpan[] starRef = new ColoredImageSpan[1]; private int shakeDp = 4; + private LinearLayout tonBalanceLayout; + private RelativeSizeSpan tonBalanceTitleSizeSpan; + private AnimatedTextView tonBalanceTitle; + private AnimatedTextView tonBalanceSubtitle; + private ButtonWithCounterView tonBalanceButton; + private double rate; - public BotStarsActivity(long botId) { + public BotStarsActivity(int type, long botId) { + this.type = type; this.bot_id = botId; - BotStarsController.getInstance(currentAccount).preloadRevenueStats(bot_id); - BotStarsController.getInstance(currentAccount).invalidateTransactions(bot_id, true); + if (type == TYPE_STARS) { + BotStarsController.getInstance(currentAccount).preloadStarsStats(bot_id); + BotStarsController.getInstance(currentAccount).invalidateTransactions(bot_id, true); + } else if (type == TYPE_TON) { + BotStarsController.getInstance(currentAccount).preloadTonStats(bot_id); + } withdrawInfo = AndroidUtilities.replaceArrows(AndroidUtilities.replaceSingleTag(getString(R.string.BotStarsWithdrawInfo), () -> { Browser.openUrl(getContext(), getString(R.string.BotStarsWithdrawInfoLink)); @@ -133,7 +162,11 @@ public View createView(Context context) { TLRPC.User bot = getMessagesController().getUser(bot_id); avatarContainer.setUserAvatar(bot, true); avatarContainer.setTitle(UserObject.getUserName(bot)); - avatarContainer.hideSubtitle(); + if (type == BotStarsActivity.TYPE_STARS) { + avatarContainer.setSubtitle(LocaleController.getString(R.string.BotStatsStars)); + } else { + avatarContainer.setSubtitle(LocaleController.getString(R.string.BotStatsTON)); + } actionBar.setBackButtonDrawable(new BackDrawable(false)); actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @@ -156,8 +189,8 @@ public void onItemClick(final int id) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure( - MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), - heightMeasureSpec + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), + heightMeasureSpec ); } }; @@ -300,10 +333,62 @@ protected boolean subTextSplitToWords() { balanceButtonsLayout.addView(adsButton, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, 1, Gravity.FILL)); balanceLayout.addView(balanceButtonsLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.TOP | Gravity.FILL_HORIZONTAL, 18, 13, 18, 0)); + tonBalanceLayout = new LinearLayout(context) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), + heightMeasureSpec + ); + } + }; + tonBalanceLayout.setOrientation(LinearLayout.VERTICAL); + tonBalanceLayout.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite, resourceProvider)); + tonBalanceLayout.setPadding(0, 0, 0, dp(17)); + + tonBalanceTitle = new AnimatedTextView(context, false, true, true); + tonBalanceTitle.setTypeface(AndroidUtilities.bold()); + tonBalanceTitle.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourceProvider)); + tonBalanceTitle.setTextSize(dp(32)); + tonBalanceTitle.setGravity(Gravity.CENTER); + tonBalanceTitleSizeSpan = new RelativeSizeSpan(65f / 96f); + tonBalanceLayout.addView(tonBalanceTitle, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 38, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 22, 15, 22, 0)); + + tonBalanceSubtitle = new AnimatedTextView(context, true, true, true); + tonBalanceSubtitle.setGravity(Gravity.CENTER); + tonBalanceSubtitle.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText, resourceProvider)); + tonBalanceSubtitle.setTextSize(dp(14)); + tonBalanceLayout.addView(tonBalanceSubtitle, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 17, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 22, 4, 22, 0)); + + tonBalanceButton = new ButtonWithCounterView(context, resourceProvider); + tonBalanceButton.setEnabled(MessagesController.getInstance(currentAccount).channelRevenueWithdrawalEnabled); + tonBalanceButton.setText(getString(R.string.MonetizationWithdraw), false); + tonBalanceButton.setVisibility(View.GONE); + tonBalanceButton.setOnClickListener(v -> { + if (!v.isEnabled() || tonBalanceButton.isLoading()) { + return; + } + TwoStepVerificationActivity passwordFragment = new TwoStepVerificationActivity(); + passwordFragment.setDelegate(1, password -> initWithdraw(false, 0, password, passwordFragment)); + tonBalanceButton.setLoading(true); + passwordFragment.preload(() -> { + tonBalanceButton.setLoading(false); + presentFragment(passwordFragment);; + }); + }); + tonBalanceLayout.addView(tonBalanceButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.TOP | Gravity.FILL_HORIZONTAL, 18, 13, 18, 0)); listView = new UniversalRecyclerView(this, this::fillItems, this::onItemClick, this::onItemLongClick); listView.setBackgroundColor(getThemedColor(Theme.key_windowBackgroundGray)); frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + listView.setOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + if (!listView.canScrollVertically(1) || isLoadingVisible()) { + loadTonTransactions(); + } + } + }); return fragmentView = frameLayout; } @@ -345,7 +430,7 @@ private void withdraw() { final long stars = balanceEditTextValue; TwoStepVerificationActivity passwordFragment = new TwoStepVerificationActivity(); - passwordFragment.setDelegate(1, password -> initWithdraw(stars, password, passwordFragment)); + passwordFragment.setDelegate(1, password -> initWithdraw(true, stars, password, passwordFragment)); balanceButton.setLoading(true); passwordFragment.preload(() -> { balanceButton.setLoading(false); @@ -355,44 +440,170 @@ private void withdraw() { private final int BALANCE = 1; + private CharSequence titleInfo; + private CharSequence proceedsInfo; + private CharSequence balanceInfo; + private boolean proceedsAvailable; + private StatisticActivity.ChartViewData impressionsChart; + private StatisticActivity.ChartViewData revenueChart; + private void fillItems(ArrayList items, UniversalAdapter adapter) { final BotStarsController s = BotStarsController.getInstance(currentAccount); - items.add(UItem.asChart(StatisticActivity.VIEW_TYPE_STACKBAR, stats_dc, revenueChartData)); - items.add(UItem.asShadow(-1, null)); - items.add(UItem.asBlackHeader(getString(R.string.BotStarsOverview))); - TLRPC.TL_payments_starsRevenueStats stats = s.getRevenueStats(bot_id); - if (stats != null && stats.status != null) { - availableValue.crypto_amount = stats.status.available_balance; - availableValue.currency = "USD"; - availableValue.amount = (long) (stats.status.available_balance * rate * 100.0); - totalValue.crypto_amount = stats.status.current_balance; - totalValue.currency = "USD"; - totalValue.amount = (long) (stats.status.current_balance * rate * 100.0); - totalProceedsValue.crypto_amount = stats.status.overall_revenue; - totalProceedsValue.currency = "USD"; - totalProceedsValue.amount = (long) (stats.status.overall_revenue * rate * 100.0); - setBalance(stats.status.available_balance, stats.status.next_withdrawal_at); - - balanceButtonsLayout.setVisibility(stats.status.withdrawal_enabled ? View.VISIBLE : View.GONE); + if (type == TYPE_STARS) { + items.add(UItem.asChart(StatisticActivity.VIEW_TYPE_STACKBAR, stats_dc, revenueChartData)); + items.add(UItem.asShadow(-1, null)); + items.add(UItem.asBlackHeader(getString(R.string.BotStarsOverview))); + TLRPC.TL_payments_starsRevenueStats stats = s.getStarsRevenueStats(bot_id); + if (stats != null && stats.status != null) { + availableValue.crypto_amount = stats.status.available_balance; + availableValue.currency = "USD"; + availableValue.amount = (long) (stats.status.available_balance * rate * 100.0); + totalValue.crypto_amount = stats.status.current_balance; + totalValue.currency = "USD"; + totalValue.amount = (long) (stats.status.current_balance * rate * 100.0); + totalProceedsValue.crypto_amount = stats.status.overall_revenue; + totalProceedsValue.currency = "USD"; + totalProceedsValue.amount = (long) (stats.status.overall_revenue * rate * 100.0); + setStarsBalance(stats.status.available_balance, stats.status.next_withdrawal_at); + + balanceButtonsLayout.setVisibility(stats.status.withdrawal_enabled ? View.VISIBLE : View.GONE); + } + items.add(UItem.asProceedOverview(availableValue)); + items.add(UItem.asProceedOverview(totalValue)); + items.add(UItem.asProceedOverview(totalProceedsValue)); + items.add(UItem.asShadow(-2, getString(R.string.BotStarsOverviewInfo))); + items.add(UItem.asBlackHeader(getString(R.string.BotStarsAvailableBalance))); + items.add(UItem.asCustom(BALANCE, balanceLayout)); + items.add(UItem.asShadow(-3, withdrawInfo)); + items.add(UItem.asFullscreenCustom(transactionsLayout, 0)); + } else if (type == TYPE_TON) { + TL_stats.TL_broadcastRevenueStats stats = s.getTONRevenueStats(bot_id, true); + if (titleInfo == null) { + titleInfo = AndroidUtilities.replaceArrows(AndroidUtilities.replaceSingleTag(formatString(R.string.BotMonetizationInfo, 50), -1, REPLACING_TAG_TYPE_LINK_NBSP, () -> { + showDialog(ChannelMonetizationLayout.makeLearnSheet(getContext(), true, resourceProvider)); + }, resourceProvider), true); + } + items.add(UItem.asCenterShadow(titleInfo)); + if (impressionsChart == null && stats != null) { + impressionsChart = StatisticActivity.createViewData(stats.top_hours_graph, getString(R.string.BotMonetizationGraphImpressions), 0); + if (impressionsChart != null) { + impressionsChart.useHourFormat = true; + } + } + if (impressionsChart != null && !impressionsChart.isEmpty) { + items.add(UItem.asChart(StatisticActivity.VIEW_TYPE_BAR_LINEAR, stats_dc, impressionsChart)); + items.add(UItem.asShadow(-1, null)); + } + if (revenueChart == null && stats != null) { + if (stats.revenue_graph != null) { + stats.revenue_graph.rate = (float) (1_000_000_000.0 / 100.0 / stats.usd_rate); + } + revenueChart = StatisticActivity.createViewData(stats.revenue_graph, getString(R.string.BotMonetizationGraphRevenue), 2); + } + if (revenueChart != null && !revenueChart.isEmpty) { + items.add(UItem.asChart(StatisticActivity.VIEW_TYPE_STACKBAR, stats_dc, revenueChart)); + items.add(UItem.asShadow(-2, null)); + } + if (!proceedsAvailable && stats != null && stats.balances != null) { + double ton_rate = stats.usd_rate; + tonAvailableValue.crypto_amount = stats.balances.available_balance; + tonAvailableValue.amount = (long) (tonAvailableValue.crypto_amount / 1_000_000_000.0 * ton_rate * 100.0); + setBalance(tonAvailableValue.crypto_amount, tonAvailableValue.amount); + tonAvailableValue.currency = "USD"; + tonLastWithdrawalValue.crypto_amount = stats.balances.current_balance; + tonLastWithdrawalValue.amount = (long) (tonLastWithdrawalValue.crypto_amount / 1_000_000_000.0 * ton_rate * 100.0); + tonLastWithdrawalValue.currency = "USD"; + tonLifetimeValue.contains1 = true; + tonLifetimeValue.crypto_amount = stats.balances.overall_revenue; + tonLifetimeValue.amount = (long) (tonLifetimeValue.crypto_amount / 1_000_000_000.0 * ton_rate * 100.0); + tonLifetimeValue.currency = "USD"; + proceedsAvailable = true; + tonBalanceButton.setVisibility(stats.balances.available_balance > 0 && stats.balances.withdrawal_enabled ? View.VISIBLE : View.GONE); + } + if (proceedsAvailable) { + items.add(UItem.asBlackHeader(getString(R.string.BotMonetizationOverview))); + items.add(UItem.asProceedOverview(tonAvailableValue)); + items.add(UItem.asProceedOverview(tonLastWithdrawalValue)); + items.add(UItem.asProceedOverview(tonLifetimeValue)); + if (proceedsInfo == null) { + final int proceedsInfoText = R.string.BotMonetizationProceedsTONInfo; + final int proceedsInfoLink = R.string.BotMonetizationProceedsTONInfoLink; + proceedsInfo = AndroidUtilities.replaceArrows(AndroidUtilities.replaceSingleTag(getString(proceedsInfoText), -1, REPLACING_TAG_TYPE_LINK_NBSP, () -> { + Browser.openUrl(getContext(), getString(proceedsInfoLink)); + }, resourceProvider), true); + } + items.add(UItem.asShadow(-4, proceedsInfo)); + } + + items.add(UItem.asBlackHeader(getString(R.string.BotMonetizationBalance))); + items.add(UItem.asCustom(tonBalanceLayout)); + if (balanceInfo == null) { + balanceInfo = AndroidUtilities.replaceArrows(AndroidUtilities.replaceSingleTag(getString(MessagesController.getInstance(currentAccount).channelRevenueWithdrawalEnabled ? R.string.BotMonetizationBalanceInfo : R.string.BotMonetizationBalanceInfoNotAvailable), -1, REPLACING_TAG_TYPE_LINK_NBSP, () -> { + Browser.openUrl(getContext(), getString(R.string.BotMonetizationBalanceInfoLink)); + }), true); + } + items.add(UItem.asShadow(-5, balanceInfo)); + if (!tonTransactionsEndReached || !tonTransactions.isEmpty()) { + items.add(UItem.asBlackHeader(getString(R.string.BotMonetizationTransactions))); + for (TL_stats.BroadcastRevenueTransaction t : tonTransactions) { + items.add(UItem.asTransaction(t)); + } + if (!tonTransactionsEndReached) { + items.add(UItem.asFlicker(1, FlickerLoadingView.DIALOG_CELL_TYPE)); + items.add(UItem.asFlicker(2, FlickerLoadingView.DIALOG_CELL_TYPE)); + items.add(UItem.asFlicker(3, FlickerLoadingView.DIALOG_CELL_TYPE)); + } + } + items.add(UItem.asShadow(-6, null)); } - items.add(UItem.asProceedOverview(availableValue)); - items.add(UItem.asProceedOverview(totalValue)); - items.add(UItem.asProceedOverview(totalProceedsValue)); - items.add(UItem.asShadow(-2, getString(R.string.BotStarsOverviewInfo))); - items.add(UItem.asBlackHeader(getString(R.string.BotStarsAvailableBalance))); - items.add(UItem.asCustom(BALANCE, balanceLayout)); - items.add(UItem.asShadow(-3, withdrawInfo)); - items.add(UItem.asFullscreenCustom(transactionsLayout, 0)); + } + + private boolean tonTransactionsLoading = false; + private boolean tonTransactionsEndReached = false; + private int tonTransactionsCount = 0; + private final ArrayList tonTransactions = new ArrayList<>(); + private void loadTonTransactions() { + if (tonTransactionsLoading || tonTransactionsEndReached) return; + tonTransactionsLoading = true; + TL_stats.TL_getBroadcastRevenueTransactions req = new TL_stats.TL_getBroadcastRevenueTransactions(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(bot_id); + req.offset = tonTransactions.size(); + req.limit = tonTransactions.isEmpty() ? 5 : 20; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (res instanceof TL_stats.TL_broadcastRevenueTransactions) { + TL_stats.TL_broadcastRevenueTransactions r = (TL_stats.TL_broadcastRevenueTransactions) res; + tonTransactionsCount = r.count; + tonTransactions.addAll(r.transactions); + tonTransactionsEndReached = tonTransactions.size() >= tonTransactionsCount || r.transactions.isEmpty(); + } else if (err != null) { + BulletinFactory.showError(err); + tonTransactionsEndReached = true; + } + tonTransactionsLoading = false; + if (listView.adapter != null) { + listView.adapter.update(true); + } + })); + } + + public boolean isLoadingVisible() { + for (int i = 0; i < listView.getChildCount(); ++i) { + if (listView.getChildAt(i) instanceof FlickerLoadingView) + return true; + } + return false; } private void onItemClick(UItem item, View view, int pos, float x, float y) { if (item.instanceOf(StarsIntroActivity.StarsTransactionView.Factory.class)) { TL_stars.StarsTransaction t = (TL_stars.StarsTransaction) item.object; StarsIntroActivity.showTransactionSheet(getContext(), true, bot_id, currentAccount, t, getResourceProvider()); + } else if (item.object instanceof TL_stats.BroadcastRevenueTransaction) { + ChannelMonetizationLayout.showTransactionSheet(getContext(), currentAccount, (TL_stats.BroadcastRevenueTransaction) item.object, bot_id, resourceProvider); } } - private void setBalance(long crypto_amount, int blockedUntil) { + private void setStarsBalance(long crypto_amount, int blockedUntil) { if (balanceTitle == null || balanceSubtitle == null) return; long amount = (long) (rate * crypto_amount * 100.0); @@ -418,6 +629,26 @@ private void setBalance(long crypto_amount, int blockedUntil) { setBalanceButtonText.run(); } + private DecimalFormat formatter; + private void setBalance(long crypto_amount, long amount) { + if (formatter == null) { + DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US); + symbols.setDecimalSeparator('.'); + formatter = new DecimalFormat("#.##", symbols); + formatter.setMinimumFractionDigits(2); + formatter.setMaximumFractionDigits(6); + formatter.setGroupingUsed(false); + } + formatter.setMaximumFractionDigits(crypto_amount / 1_000_000_000.0 > 1.5 ? 2 : 6); + SpannableStringBuilder ssb = new SpannableStringBuilder(replaceTON("TON " + formatter.format(crypto_amount / 1_000_000_000.0), tonBalanceTitle.getPaint(), .9f, true)); + int index = TextUtils.indexOf(ssb, "."); + if (index >= 0) { + ssb.setSpan(tonBalanceTitleSizeSpan, index, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + tonBalanceTitle.setText(ssb); + tonBalanceSubtitle.setText("≈" + BillingController.getInstance().formatCurrency(amount, "USD")); + } + private SpannableStringBuilder lock; private Runnable setBalanceButtonText = () -> { final int now = getConnectionsManager().getCurrentTime(); @@ -478,7 +709,7 @@ public boolean onFragmentCreate() { } private void checkStats() { - TLRPC.TL_payments_starsRevenueStats stats = BotStarsController.getInstance(currentAccount).getRevenueStats(bot_id); + TLRPC.TL_payments_starsRevenueStats stats = BotStarsController.getInstance(currentAccount).getStarsRevenueStats(bot_id); if (stats == lastStats && (stats == null ? null : stats.status) == lastStatsStatus) { return; } @@ -493,7 +724,7 @@ private void checkStats() { revenueChartData.chartData.lines.get(0).colorKey = Theme.key_color_yellow; revenueChartData.chartData.yRate = (float) (1.0 / rate / 100.0); } - setBalance(stats.status.available_balance, stats.status.next_withdrawal_at); + setStarsBalance(stats.status.available_balance, stats.status.next_withdrawal_at); if (listView != null) { listView.adapter.update(true); } @@ -645,6 +876,137 @@ public void onStopNestedScroll(View child) { } } + private void initWithdraw(boolean stars, long stars_amount, TLRPC.InputCheckPasswordSRP password, TwoStepVerificationActivity passwordFragment) { + Activity parentActivity = getParentActivity(); + TLRPC.User currentUser = UserConfig.getInstance(currentAccount).getCurrentUser(); + if (parentActivity == null || currentUser == null) return; + + TLObject r; + if (stars) { + TLRPC.TL_payments_getStarsRevenueWithdrawalUrl req = new TLRPC.TL_payments_getStarsRevenueWithdrawalUrl(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(bot_id); + req.password = password != null ? password : new TLRPC.TL_inputCheckPasswordEmpty(); + req.stars = stars_amount; + r = req; + } else { + TL_stats.TL_getBroadcastRevenueWithdrawalUrl req = new TL_stats.TL_getBroadcastRevenueWithdrawalUrl(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(bot_id); + req.password = password != null ? password : new TLRPC.TL_inputCheckPasswordEmpty(); + r = req; + } + ConnectionsManager.getInstance(currentAccount).sendRequest(r, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (error != null) { + if ("PASSWORD_MISSING".equals(error.text) || error.text.startsWith("PASSWORD_TOO_FRESH_") || error.text.startsWith("SESSION_TOO_FRESH_")) { + if (passwordFragment != null) { + passwordFragment.needHideProgress(); + } + AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity); + builder.setTitle(LocaleController.getString(R.string.EditAdminTransferAlertTitle)); + + LinearLayout linearLayout = new LinearLayout(parentActivity); + linearLayout.setPadding(AndroidUtilities.dp(24), AndroidUtilities.dp(2), AndroidUtilities.dp(24), 0); + linearLayout.setOrientation(LinearLayout.VERTICAL); + builder.setView(linearLayout); + + TextView messageTextView = new TextView(parentActivity); + messageTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + messageTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); + messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.getString(R.string.WithdrawChannelAlertText))); + linearLayout.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + LinearLayout linearLayout2 = new LinearLayout(parentActivity); + linearLayout2.setOrientation(LinearLayout.HORIZONTAL); + linearLayout.addView(linearLayout2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 11, 0, 0)); + + ImageView dotImageView = new ImageView(parentActivity); + dotImageView.setImageResource(R.drawable.list_circle); + dotImageView.setPadding(LocaleController.isRTL ? AndroidUtilities.dp(11) : 0, AndroidUtilities.dp(9), LocaleController.isRTL ? 0 : AndroidUtilities.dp(11), 0); + dotImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogTextBlack), PorterDuff.Mode.MULTIPLY)); + + messageTextView = new TextView(parentActivity); + messageTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + messageTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); + messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.getString(R.string.EditAdminTransferAlertText1))); + if (LocaleController.isRTL) { + linearLayout2.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + linearLayout2.addView(dotImageView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.RIGHT)); + } else { + linearLayout2.addView(dotImageView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + linearLayout2.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } + + linearLayout2 = new LinearLayout(parentActivity); + linearLayout2.setOrientation(LinearLayout.HORIZONTAL); + linearLayout.addView(linearLayout2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 11, 0, 0)); + + dotImageView = new ImageView(parentActivity); + dotImageView.setImageResource(R.drawable.list_circle); + dotImageView.setPadding(LocaleController.isRTL ? AndroidUtilities.dp(11) : 0, AndroidUtilities.dp(9), LocaleController.isRTL ? 0 : AndroidUtilities.dp(11), 0); + dotImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogTextBlack), PorterDuff.Mode.MULTIPLY)); + + messageTextView = new TextView(parentActivity); + messageTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + messageTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); + messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.getString(R.string.EditAdminTransferAlertText2))); + if (LocaleController.isRTL) { + linearLayout2.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + linearLayout2.addView(dotImageView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.RIGHT)); + } else { + linearLayout2.addView(dotImageView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + linearLayout2.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } + + if ("PASSWORD_MISSING".equals(error.text)) { + builder.setPositiveButton(LocaleController.getString(R.string.EditAdminTransferSetPassword), (dialogInterface, i) -> presentFragment(new TwoStepVerificationSetupActivity(TwoStepVerificationSetupActivity.TYPE_INTRO, null))); + builder.setNegativeButton(LocaleController.getString(R.string.Cancel), null); + } else { + messageTextView = new TextView(parentActivity); + messageTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + messageTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); + messageTextView.setText(LocaleController.getString(R.string.EditAdminTransferAlertText3)); + linearLayout.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 11, 0, 0)); + + builder.setNegativeButton(LocaleController.getString(R.string.OK), null); + } + if (passwordFragment != null) { + passwordFragment.showDialog(builder.create()); + } else { + showDialog(builder.create()); + } + } else if ("SRP_ID_INVALID".equals(error.text)) { + TLRPC.TL_account_getPassword getPasswordReq = new TLRPC.TL_account_getPassword(); + ConnectionsManager.getInstance(currentAccount).sendRequest(getPasswordReq, (response2, error2) -> AndroidUtilities.runOnUIThread(() -> { + if (error2 == null) { + TLRPC.account_Password currentPassword = (TLRPC.account_Password) response2; + passwordFragment.setCurrentPasswordInfo(null, currentPassword); + TwoStepVerificationActivity.initPasswordNewAlgo(currentPassword); + initWithdraw(stars, stars_amount, passwordFragment.getNewSrpPassword(), passwordFragment); + } + }), ConnectionsManager.RequestFlagWithoutLogin); + } else { + if (passwordFragment != null) { + passwordFragment.needHideProgress(); + passwordFragment.finishFragment(); + } + BulletinFactory.showError(error); + } + } else { + passwordFragment.needHideProgress(); + passwordFragment.finishFragment(); + if (response instanceof TL_stats.TL_broadcastRevenueWithdrawalUrl) { + Browser.openUrl(getContext(), ((TL_stats.TL_broadcastRevenueWithdrawalUrl) response).url); + } else if (response instanceof TLRPC.TL_payments_starsRevenueWithdrawalUrl) { + balanceEditTextAll = true; + Browser.openUrl(getContext(), ((TLRPC.TL_payments_starsRevenueWithdrawalUrl) response).url); + } + } + })); + } + private void initWithdraw(long stars, TLRPC.InputCheckPasswordSRP password, TwoStepVerificationActivity passwordFragment) { Activity parentActivity = getParentActivity(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stars/BotStarsController.java b/TMessagesProj/src/main/java/org/telegram/ui/Stars/BotStarsController.java index c9ccb0a853d..fd30ccaef42 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stars/BotStarsController.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stars/BotStarsController.java @@ -48,49 +48,62 @@ private BotStarsController(int account) { currentAccount = account; } - private final HashMap lastLoadedStats = new HashMap<>(); - private final HashMap stats = new HashMap<>(); + private final HashMap lastLoadedBotStarsStats = new HashMap<>(); + private final HashMap botStarsStats = new HashMap<>(); - private final HashMap lastLoadedChannelStats = new HashMap<>(); - private final HashMap channelStats = new HashMap<>(); + private final HashMap lastLoadedTonStats = new HashMap<>(); + private final HashMap tonStats = new HashMap<>(); - public long getBalance(long did) { - TLRPC.TL_payments_starsRevenueStats botStats = getRevenueStats(did); + public long getBotStarsBalance(long did) { + TLRPC.TL_payments_starsRevenueStats botStats = getStarsRevenueStats(did); return botStats == null ? 0 : botStats.status.current_balance; } - public long getChannelBalance(long did) { - TL_stats.TL_broadcastRevenueStats botStats = getChannelRevenueStats(did, false); + public long getTONBalance(long did) { + TL_stats.TL_broadcastRevenueStats botStats = getTONRevenueStats(did, false); return botStats == null || botStats.balances == null ? 0 : botStats.balances.current_balance; } public long getAvailableBalance(long did) { - TLRPC.TL_payments_starsRevenueStats botStats = getRevenueStats(did); + TLRPC.TL_payments_starsRevenueStats botStats = getStarsRevenueStats(did); return botStats == null ? 0 : botStats.status.available_balance; } - public boolean isBalanceAvailable(long did) { - return getRevenueStats(did) != null; + public boolean isStarsBalanceAvailable(long did) { + return getStarsRevenueStats(did) != null; } - public TLRPC.TL_payments_starsRevenueStats getRevenueStats(long did) { - return getRevenueStats(did, false); + public boolean isTONBalanceAvailable(long did) { + return getTONRevenueStats(did, false) != null; } - public boolean hasStars(long did) { - TLRPC.TL_payments_starsRevenueStats stats = getRevenueStats(did); + public TLRPC.TL_payments_starsRevenueStats getStarsRevenueStats(long did) { + return getStarsRevenueStats(did, false); + } + + public boolean botHasStars(long did) { + TLRPC.TL_payments_starsRevenueStats stats = getStarsRevenueStats(did); return stats != null && stats.status != null && (stats.status.available_balance > 0 || stats.status.overall_revenue > 0 || stats.status.current_balance > 0); } - public void preloadRevenueStats(long did) { - Long lastLoaded = lastLoadedStats.get(did); - TLRPC.TL_payments_starsRevenueStats botStats = stats.get(did); - getRevenueStats(did, lastLoaded == null || System.currentTimeMillis() - lastLoaded > 1000 * 30); + public boolean botHasTON(long did) { + TL_stats.TL_broadcastRevenueStats stats = getTONRevenueStats(did, false); + return stats != null && (stats.balances.current_balance > 0 || stats.balances.available_balance > 0 || stats.balances.overall_revenue > 0); + } + + public void preloadStarsStats(long did) { + Long lastLoaded = lastLoadedBotStarsStats.get(did); + getStarsRevenueStats(did, lastLoaded == null || System.currentTimeMillis() - lastLoaded > 1000 * 30); } - public TLRPC.TL_payments_starsRevenueStats getRevenueStats(long did, boolean force) { - Long lastLoaded = lastLoadedStats.get(did); - TLRPC.TL_payments_starsRevenueStats botStats = stats.get(did); + public void preloadTonStats(long did) { + Long lastLoaded = lastLoadedTonStats.get(did); + getTONRevenueStats(did, lastLoaded == null || System.currentTimeMillis() - lastLoaded > 1000 * 30); + } + + public TLRPC.TL_payments_starsRevenueStats getStarsRevenueStats(long did, boolean force) { + Long lastLoaded = lastLoadedBotStarsStats.get(did); + TLRPC.TL_payments_starsRevenueStats botStats = botStarsStats.get(did); if (lastLoaded == null || System.currentTimeMillis() - lastLoaded > 1000 * 60 * 5 || force) { TLRPC.TL_payments_getStarsRevenueStats req = new TLRPC.TL_payments_getStarsRevenueStats(); req.dark = Theme.isCurrentThemeDark(); @@ -98,35 +111,39 @@ public TLRPC.TL_payments_starsRevenueStats getRevenueStats(long did, boolean for ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { if (res instanceof TLRPC.TL_payments_starsRevenueStats) { TLRPC.TL_payments_starsRevenueStats r = (TLRPC.TL_payments_starsRevenueStats) res; - stats.put(did, r); + botStarsStats.put(did, r); } else { - stats.put(did, null); + botStarsStats.put(did, null); } - lastLoadedStats.put(did, System.currentTimeMillis()); + lastLoadedBotStarsStats.put(did, System.currentTimeMillis()); NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.botStarsUpdated, did); })); } return botStats; } - public TL_stats.TL_broadcastRevenueStats getChannelRevenueStats(long did, boolean force) { - Long lastLoaded = lastLoadedChannelStats.get(did); - TL_stats.TL_broadcastRevenueStats botStats = channelStats.get(did); + public TL_stats.TL_broadcastRevenueStats getTONRevenueStats(long did, boolean force) { + Long lastLoaded = lastLoadedTonStats.get(did); + TL_stats.TL_broadcastRevenueStats botStats = tonStats.get(did); if (lastLoaded == null || System.currentTimeMillis() - lastLoaded > 1000 * 60 * 5 || force) { TL_stats.TL_getBroadcastRevenueStats req = new TL_stats.TL_getBroadcastRevenueStats(); req.dark = Theme.isCurrentThemeDark(); - req.channel = MessagesController.getInstance(currentAccount).getInputChannel(-did); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(did); + final int stats_dc; TLRPC.ChatFull chatFull = MessagesController.getInstance(currentAccount).getChatFull(-did); - if (chatFull == null) return botStats; - final int stats_dc = chatFull.stats_dc; + if (chatFull != null) { + stats_dc = chatFull.stats_dc; + } else { + stats_dc = ConnectionsManager.DEFAULT_DATACENTER_ID; + } ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { if (res instanceof TL_stats.TL_broadcastRevenueStats) { TL_stats.TL_broadcastRevenueStats r = (TL_stats.TL_broadcastRevenueStats) res; - channelStats.put(did, r); + tonStats.put(did, r); } else { - channelStats.put(did, null); + tonStats.put(did, null); } - lastLoadedChannelStats.put(did, System.currentTimeMillis()); + lastLoadedTonStats.put(did, System.currentTimeMillis()); NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.botStarsUpdated, did); }), null, null, 0, stats_dc, ConnectionsManager.ConnectionTypeGeneric, true); } @@ -142,7 +159,7 @@ public void onUpdate(TLRPC.TL_updateStarsRevenueStatus update) { ChannelMonetizationLayout.instance.reloadTransactions(); } } else { - TLRPC.TL_payments_starsRevenueStats s = getRevenueStats(dialogId, true); + TLRPC.TL_payments_starsRevenueStats s = getStarsRevenueStats(dialogId, true); if (s != null) { s.status = update.status; NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.botStarsUpdated, dialogId); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stars/StarsController.java b/TMessagesProj/src/main/java/org/telegram/ui/Stars/StarsController.java index cb401eaa881..805f294fa3c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stars/StarsController.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stars/StarsController.java @@ -60,6 +60,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Calendar; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -579,7 +580,7 @@ public void invalidateSubscriptions(boolean load) { public void loadSubscriptions() { if (subscriptionsLoading || subscriptionsEndReached) return; subscriptionsLoading = true; - TL_stars.TL_getStarsSubscriptions req = new TL_stars.TL_getStarsSubscriptions(); + final TL_stars.TL_getStarsSubscriptions req = new TL_stars.TL_getStarsSubscriptions(); req.peer = new TLRPC.TL_inputPeerSelf(); req.offset = subscriptionsOffset; if (req.offset == null) { @@ -1152,6 +1153,7 @@ public Runnable pay(MessageObject messageObject, Runnable whenShown) { public void openPaymentForm(MessageObject messageObject, TLRPC.InputInvoice inputInvoice, TLRPC.TL_payments_paymentFormStars form, Runnable whenShown, Utilities.Callback whenAllDone) { if (form == null || form.invoice == null || paymentFormOpened) return; + MessagesController.getInstance(currentAccount).putUsers(form.users, false); final Context context = LaunchActivity.instance != null ? LaunchActivity.instance : ApplicationLoader.applicationContext; final Theme.ResourcesProvider resourcesProvider = getResourceProvider(); @@ -1183,11 +1185,17 @@ public void openPaymentForm(MessageObject messageObject, TLRPC.InputInvoice inpu messageObject.getDialogId() ) : form.bot_id; final String bot; + final boolean isBot, isBiz; if (dialogId >= 0) { - bot = UserObject.getUserName(MessagesController.getInstance(currentAccount).getUser(dialogId)); + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + bot = UserObject.getUserName(user); + isBot = UserObject.isBot(user); + isBiz = !UserObject.isBot(user); } else { TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-dialogId); bot = chat == null ? "" : chat.title; + isBot = false; + isBiz = false; } final String product = form.title; @@ -1195,8 +1203,9 @@ public void openPaymentForm(MessageObject messageObject, TLRPC.InputInvoice inpu whenShown.run(); } + final int subscription_period = form.invoice.subscription_period; final boolean[] allDone = new boolean[] { false }; - StarsIntroActivity.openConfirmPurchaseSheet(context, resourcesProvider, currentAccount, messageObject, dialogId, product, stars, form.photo, whenDone -> { + StarsIntroActivity.openConfirmPurchaseSheet(context, resourcesProvider, currentAccount, messageObject, dialogId, product, stars, form.photo, subscription_period, whenDone -> { if (balance < stars) { if (!MessagesController.getInstance(currentAccount).starsPurchaseAvailable()) { paymentFormOpened = false; @@ -1211,10 +1220,13 @@ public void openPaymentForm(MessageObject messageObject, TLRPC.InputInvoice inpu return; } final boolean[] purchased = new boolean[] { false }; - StarsIntroActivity.StarsNeededSheet sheet = new StarsIntroActivity.StarsNeededSheet(context, resourcesProvider, stars, StarsIntroActivity.StarsNeededSheet.TYPE_BOT, bot, () -> { + StarsIntroActivity.StarsNeededSheet sheet = new StarsIntroActivity.StarsNeededSheet(context, resourcesProvider, stars, isBiz ? StarsIntroActivity.StarsNeededSheet.TYPE_BIZ : StarsIntroActivity.StarsNeededSheet.TYPE_BOT, bot, () -> { purchased[0] = true; payAfterConfirmed(messageObject, inputInvoice, form, success -> { allDone[0] = true; + if (subscription_period > 0) { + invalidateSubscriptions(true); + } if (whenAllDone != null) { whenAllDone.run(success ? "paid" : "failed"); } @@ -1236,6 +1248,9 @@ public void openPaymentForm(MessageObject messageObject, TLRPC.InputInvoice inpu sheet.show(); } else { payAfterConfirmed(messageObject, inputInvoice, form, success -> { + if (subscription_period > 0) { + invalidateSubscriptions(true); + } if (whenDone != null) { whenDone.run(true); } @@ -1375,6 +1390,7 @@ private void payAfterConfirmed(MessageObject messageObject, TLRPC.InputInvoice i bot = chat == null ? "" : chat.title; } final String product = form.title; + final int subscription_period = form.invoice.subscription_period; TL_stars.TL_payments_sendStarsForm req2 = new TL_stars.TL_payments_sendStarsForm(); req2.form_id = form.form_id; @@ -1395,6 +1411,8 @@ private void payAfterConfirmed(MessageObject messageObject, TLRPC.InputInvoice i if (media) { Drawable starDrawable = context.getResources().getDrawable(R.drawable.star_small_inner).mutate(); b.createSimpleBulletin(starDrawable, getString(R.string.StarsMediaPurchaseCompleted), AndroidUtilities.replaceTags(formatPluralString("StarsMediaPurchaseCompletedInfo", (int) stars, bot))).show(); + } else if (subscription_period > 0) { + b.createSimpleBulletin(R.raw.stars_send, getString(R.string.StarsBotSubscriptionCompleted), AndroidUtilities.replaceTags(formatPluralString("StarsBotSubscriptionCompletedInfo", (int) stars, product, bot))).show(); } else { b.createSimpleBulletin(R.raw.stars_send, getString(R.string.StarsPurchaseCompleted), AndroidUtilities.replaceTags(formatPluralString("StarsPurchaseCompletedInfo", (int) stars, product, bot))).show(); } @@ -1998,7 +2016,8 @@ public StarsController.PendingPaidReactions sendPaidReaction( TLRPC.Chat chat = chatActivity.getMessagesController().getChat(-dialogId); name = chat == null ? "" : chat.title; } - new StarsIntroActivity.StarsNeededSheet(chatActivity.getContext(), chatActivity.getResourceProvider(), totalStars, StarsIntroActivity.StarsNeededSheet.TYPE_REACTIONS, name, () -> { + if (context == null) return null; + new StarsIntroActivity.StarsNeededSheet(context, chatActivity.getResourceProvider(), totalStars, StarsIntroActivity.StarsNeededSheet.TYPE_REACTIONS, name, () -> { sendPaidReaction(messageObject, chatActivity, totalStars, true, true, anonymous); }).show(); return null; @@ -2081,6 +2100,7 @@ public long getPendingPaidReactions(long dialogId, int messageId) { public int giftsHash; public long giftsRemoteTime; public final ArrayList gifts = new ArrayList<>(); + public final ArrayList birthdaySortedGifts = new ArrayList<>(); public void invalidateStarGifts() { giftsLoaded = false; @@ -2093,13 +2113,14 @@ public void loadStarGifts() { if (giftsLoading || giftsLoaded && (System.currentTimeMillis() - giftsRemoteTime) < 1000 * 60 * 5) return; giftsLoading = true; - final SharedPreferences prefs = MessagesController.getInstance(currentAccount).getMainSettings(); - if (!giftsCacheLoaded) { getStarGiftsCached((giftsCached, hash, time) -> { giftsCacheLoaded = true; gifts.clear(); gifts.addAll(giftsCached); + birthdaySortedGifts.clear(); + birthdaySortedGifts.addAll(gifts); + Collections.sort(birthdaySortedGifts, (a, b) -> (b.birthday ? 1 : 0) - (a.birthday ? 1 : 0)); giftsHash = hash; giftsRemoteTime = time; giftsLoading = false; @@ -2115,6 +2136,9 @@ public void loadStarGifts() { final TL_stars.TL_starGifts res = (TL_stars.TL_starGifts) giftsRemote; gifts.clear(); gifts.addAll(res.gifts); + birthdaySortedGifts.clear(); + birthdaySortedGifts.addAll(gifts); + Collections.sort(birthdaySortedGifts, (a, b) -> (b.birthday ? 1 : 0) - (a.birthday ? 1 : 0)); giftsHash = res.hash; giftsRemoteTime = System.currentTimeMillis(); NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.starGiftsLoaded); @@ -2371,6 +2395,11 @@ public void buyStarGift(Activity activity, TL_stars.StarGift gift, boolean anony }); fragment.presentFragment(chatActivity); } + + MessagesController.getInstance(currentAccount).getMainSettings().edit() + .putBoolean("show_gift_for_" + user_id, true) + .putBoolean(Calendar.getInstance().get(Calendar.YEAR) + "show_gift_for_" + user_id, true) + .apply(); if (LaunchActivity.instance != null && LaunchActivity.instance.getFireworksOverlay() != null) { LaunchActivity.instance.getFireworksOverlay().start(true); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stars/StarsIntroActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/Stars/StarsIntroActivity.java index ec6eff33d79..3e5084f9b91 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stars/StarsIntroActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stars/StarsIntroActivity.java @@ -1,7 +1,6 @@ package org.telegram.ui.Stars; import static org.telegram.messenger.AndroidUtilities.dp; -import static org.telegram.messenger.AndroidUtilities.translitSafe; import static org.telegram.messenger.LocaleController.formatPluralString; import static org.telegram.messenger.LocaleController.formatPluralStringComma; import static org.telegram.messenger.LocaleController.formatPluralStringSpaced; @@ -22,6 +21,7 @@ import android.graphics.RectF; import android.graphics.Typeface; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.text.Editable; @@ -82,6 +82,7 @@ import org.telegram.messenger.WebFile; import org.telegram.messenger.browser.Browser; import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.tl.TL_stars; import org.telegram.tgnet.tl.TL_stories; @@ -1413,7 +1414,7 @@ public static CombinedDrawable getPlatformDrawable(String platform, int sz) { public void set(TL_stars.StarsTransaction transaction, boolean bot, boolean divider) { long did = DialogObject.getPeerDialogId(transaction.peer.peer); - threeLines = did != 0 || transaction.subscription || transaction.stargift != null || transaction.gift && transaction.peer instanceof TL_stars.TL_starsTransactionPeerFragment; + threeLines = did != 0 || transaction.subscription || transaction.floodskip || transaction.stargift != null || transaction.gift && transaction.peer instanceof TL_stars.TL_starsTransactionPeerFragment; titleTextViewParams.bottomMargin = threeLines ? 0 : dp(4.33f); subtitleTextView.setVisibility(threeLines ? View.VISIBLE : View.GONE); dateTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, threeLines ? 13 : 14); @@ -1436,15 +1437,7 @@ public void set(TL_stars.StarsTransaction transaction, boolean bot, boolean divi imageView.setTranslationY(0); imageView2.setVisibility(GONE); imageView.setRoundRadius(dp(46)); - if (transaction.stargift != null) { - setGiftImage(imageView.getImageReceiver(), transaction.stargift, 46); - titleTextView.setText(MessagesController.getInstance(currentAccount).getPeerName(DialogObject.getPeerDialogId(transaction.peer.peer))); - if (transaction.refund) { - subtitleTextView.setText(LocaleController.getString(transaction.stars > 0 ? R.string.Gift2TransactionRefundedSent : R.string.Gift2TransactionRefundedConverted)); - } else { - subtitleTextView.setText(LocaleController.getString(transaction.stars > 0 ? R.string.Gift2TransactionConverted : R.string.Gift2TransactionSent)); - } - } else if (did != 0) { + if (did != 0) { boolean deleted = false; String username; if (UserObject.isService(did)) { @@ -1453,21 +1446,30 @@ public void set(TL_stars.StarsTransaction transaction, boolean bot, boolean divi } else if (did >= 0) { TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(did); deleted = user == null; - if (transaction.photo == null) { - avatarDrawable.setInfo(user); - imageView.setForUserOrChat(user, avatarDrawable); - } + avatarDrawable.setInfo(user); + imageView.setForUserOrChat(user, avatarDrawable); username = UserObject.getUserName(user); } else { TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-did); deleted = chat == null; - if (transaction.photo == null) { - avatarDrawable.setInfo(chat); - imageView.setForUserOrChat(chat, avatarDrawable); - } + avatarDrawable.setInfo(chat); + imageView.setForUserOrChat(chat, avatarDrawable); username = chat == null ? "" : chat.title; } - if (transaction.subscription) { + if (transaction.stargift != null) { + ImageReceiverSpan span = new ImageReceiverSpan(subtitleTextView, currentAccount, 16); + span.setRoundRadius(4); + span.enableShadow(false); + SpannableString spanString = new SpannableString("x"); + spanString.setSpan(span, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + setGiftImage(span.imageReceiver, transaction.stargift, 16); + titleTextView.setText(username); + if (transaction.refund) { + subtitleTextView.setText(TextUtils.concat(spanString, " ", LocaleController.getString(transaction.stars > 0 ? R.string.Gift2TransactionRefundedSent : R.string.Gift2TransactionRefundedConverted))); + } else { + subtitleTextView.setText(TextUtils.concat(spanString, " ", LocaleController.getString(transaction.stars > 0 ? R.string.Gift2TransactionConverted : R.string.Gift2TransactionSent))); + } + } else if (transaction.subscription) { titleTextView.setText(username); if (transaction.subscription_period == StarsController.PERIOD_MONTHLY) { subtitleTextView.setVisibility(VISIBLE); @@ -1478,17 +1480,17 @@ public void set(TL_stars.StarsTransaction transaction, boolean bot, boolean divi subtitleTextView.setText(String.format(Locale.US, "%s subscription fee", period)); } } else if (transaction.gift) { - titleTextView.setText(LocaleController.getString(R.string.StarsGiftReceived)); + titleTextView.setText(username); subtitleTextView.setVisibility(deleted ? GONE : VISIBLE); - subtitleTextView.setText(username); + subtitleTextView.setText(LocaleController.getString(R.string.StarsGiftReceived)); } else if ((transaction.flags & 8192) != 0) { - titleTextView.setText(LocaleController.getString(R.string.StarsGiveawayPrizeReceived)); + titleTextView.setText(username); subtitleTextView.setVisibility(deleted ? GONE : VISIBLE); - subtitleTextView.setText(username); + subtitleTextView.setText(LocaleController.getString(R.string.StarsGiveawayPrizeReceived)); } else if (transaction.reaction) { - titleTextView.setText(LocaleController.getString(R.string.StarsReactionsSent)); + titleTextView.setText(username); subtitleTextView.setVisibility(deleted ? GONE : VISIBLE); - subtitleTextView.setText(username); + subtitleTextView.setText(LocaleController.getString(R.string.StarsReactionsSent)); } else if (!transaction.extended_media.isEmpty()) { if (bot) { titleTextView.setText(username); @@ -1521,15 +1523,24 @@ public void set(TL_stars.StarsTransaction transaction, boolean bot, boolean divi imageView.setTranslationY((i - imageViewCount / 2f) * dp(4.33f)); } } else if (transaction.photo != null) { - titleTextView.setText(transaction.title != null ? transaction.title : ""); + ImageReceiverSpan span = new ImageReceiverSpan(subtitleTextView, currentAccount, 14); + span.setRoundRadius(4); + span.enableShadow(false); + SpannableString spanString = new SpannableString("x"); + spanString.setSpan(span, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + span.imageReceiver.setImage(ImageLocation.getForWebFile(WebFile.createWithWebDocument(transaction.photo)), "14_14", null, null, 0, 0); + titleTextView.setText(username); subtitleTextView.setVisibility(deleted ? GONE : VISIBLE); - subtitleTextView.setText(username); - imageView.setImage(ImageLocation.getForWebFile(WebFile.createWithWebDocument(transaction.photo)), "46_46", null, 0, null); + subtitleTextView.setText(Emoji.replaceEmoji(TextUtils.concat(spanString, " ", transaction.title != null ? transaction.title : ""), subtitleTextView.getPaint().getFontMetricsInt(), false)); } else { - titleTextView.setText(transaction.title != null ? transaction.title : ""); + titleTextView.setText(username); subtitleTextView.setVisibility(deleted ? GONE : VISIBLE); - subtitleTextView.setText(username); + subtitleTextView.setText(Emoji.replaceEmoji(transaction.title != null ? transaction.title : "", subtitleTextView.getPaint().getFontMetricsInt(), false)); } + } else if (transaction.floodskip) { + titleTextView.setText(getString(R.string.StarsTransactionFloodskip)); + subtitleTextView.setText(LocaleController.formatPluralStringComma("StarsTransactionFloodskipMessages", transaction.floodskip_number)); + imageView.setImageDrawable(getPlatformDrawable("api")); } else if (transaction.peer instanceof TL_stars.TL_starsTransactionPeerAppStore) { titleTextView.setText(getString(R.string.StarsTransactionInApp)); imageView.setImageDrawable(getPlatformDrawable("ios")); @@ -1635,11 +1646,13 @@ public static class StarsSubscriptionView extends LinearLayout { public final BackupImageView imageView; public final LinearLayout textLayout; public final SimpleTextView titleView; + public final TextView productView; public final TextView subtitleView; public final LinearLayout priceLayout; public final TextView priceTitleView; public final TextView priceSubtitleView; + private boolean threeLines; private boolean needDivider; public StarsSubscriptionView(Context context, int currentAccount, Theme.ResourcesProvider resourcesProvider) { @@ -1661,8 +1674,15 @@ public StarsSubscriptionView(Context context, int currentAccount, Theme.Resource titleView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); titleView.setTextSize(16); titleView.setTypeface(AndroidUtilities.bold()); + NotificationCenter.listenEmojiLoading(titleView); textLayout.addView(titleView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, 2)); + productView = new TextView(context); + productView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + productView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); + productView.setVisibility(View.GONE); + textLayout.addView(productView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, 1)); + subtitleView = new TextView(context); subtitleView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider)); subtitleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); @@ -1689,20 +1709,53 @@ public StarsSubscriptionView(Context context, int currentAccount, Theme.Resource public void set(TL_stars.StarsSubscription subscription, boolean divider) { long dialogId = DialogObject.getPeerDialogId(subscription.peer); - TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-dialogId); - if (chat == null) return; + threeLines = !TextUtils.isEmpty(subscription.title); - AvatarDrawable avatarDrawable = new AvatarDrawable(); - avatarDrawable.setInfo(chat); - imageView.setForUserOrChat(chat, avatarDrawable); + String name = ""; + boolean business; + if (dialogId < 0) { + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-dialogId); + AvatarDrawable avatarDrawable = new AvatarDrawable(); + avatarDrawable.setInfo(chat); + imageView.setForUserOrChat(chat, avatarDrawable); + name = chat != null ? chat.title : null; + business = false; + } else { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + AvatarDrawable avatarDrawable = new AvatarDrawable(); + avatarDrawable.setInfo(user); + imageView.setForUserOrChat(user, avatarDrawable); + name = UserObject.getUserName(user); + business = !UserObject.isBot(user); + } + + final long now = ConnectionsManager.getInstance(currentAccount).getCurrentTime(); + titleView.setText(Emoji.replaceEmoji(name, titleView.getPaint().getFontMetricsInt(), false)); + + if (!TextUtils.isEmpty(subscription.title)) { + productView.setVisibility(View.VISIBLE); + SpannableStringBuilder productName = new SpannableStringBuilder(); + if (subscription.photo != null) { + ImageReceiverSpan span = new ImageReceiverSpan(productView, currentAccount, 14); + span.setRoundRadius(4); + span.enableShadow(false); + SpannableString spanString = new SpannableString("x"); + spanString.setSpan(span, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + span.imageReceiver.setImage(ImageLocation.getForWebFile(WebFile.createWithWebDocument(subscription.photo)), "14_14", null, null, 0, 0); + productName.append(spanString).append(" "); + } + productName.append(Emoji.replaceEmoji(subscription.title, titleView.getPaint().getFontMetricsInt(), false)); + productView.setText(productName); + } else { + productView.setVisibility(View.GONE); + } - long now = ConnectionsManager.getInstance(currentAccount).getCurrentTime(); - titleView.setText(chat.title); - if (subscription.canceled) { + subtitleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, threeLines ? 13 : 14); + if (subscription.canceled || subscription.bot_canceled) { subtitleView.setText(formatString(subscription.until_date < now ? R.string.StarsSubscriptionExpired : R.string.StarsSubscriptionExpires, LocaleController.formatDateChat(subscription.until_date))); priceTitleView.setVisibility(View.GONE); priceSubtitleView.setTextColor(Theme.getColor(Theme.key_color_red, resourcesProvider)); - priceSubtitleView.setText(LocaleController.getString(R.string.StarsSubscriptionStatusCancelled)); + priceSubtitleView.setText(LocaleController.getString(subscription.bot_canceled ? (business ? R.string.StarsSubscriptionStatusBizCancelled : R.string.StarsSubscriptionStatusBotCancelled) : R.string.StarsSubscriptionStatusCancelled)); } else if (subscription.until_date < now) { subtitleView.setText(formatString(R.string.StarsSubscriptionExpired, LocaleController.formatDateChat(subscription.until_date))); priceTitleView.setVisibility(View.GONE); @@ -1729,7 +1782,7 @@ public void set(TL_stars.StarsSubscription subscription, boolean divider) { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure( MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(dp(58), MeasureSpec.EXACTLY) + MeasureSpec.makeMeasureSpec(dp(threeLines ? 68 : 58), MeasureSpec.EXACTLY) ); } @@ -1786,6 +1839,7 @@ public static BottomSheet openConfirmPurchaseSheet( String purchase, long stars, TLRPC.WebDocument photo, + int subscription_period, Utilities.Callback> whenConfirmed, Runnable whenDismissed ) { @@ -1866,10 +1920,25 @@ protected void onDetachedFromWindow() { imageView.setForUserOrChat(user, avatarDrawable); topView.addView(imageView, LayoutHelper.createFrame(80, 80, Gravity.CENTER)); } else { + FrameLayout imageViewLayout = new FrameLayout(context); BackupImageView imageView = new BackupImageView(context); - imageView.setRoundRadius(dp(80)); + imageView.setRoundRadius(dp(18)); imageView.setImage(ImageLocation.getForWebFile(WebFile.createWithWebDocument(photo)), "80_80", null, 0, null); - topView.addView(imageView, LayoutHelper.createFrame(80, 80, Gravity.CENTER)); + imageViewLayout.addView(imageView, LayoutHelper.createFrame(80, 80, Gravity.TOP)); + topView.addView(imageViewLayout, LayoutHelper.createFrame(80, 87, Gravity.CENTER)); + + TextView priceView = new TextView(context); + priceView.setTypeface(AndroidUtilities.getTypeface("fonts/num.otf")); + priceView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); + priceView.setTextColor(0xFFFFFFFF); + priceView.setText(replaceStars("XTR " + LocaleController.formatNumber((int) stars, ','), .85f)); + priceView.setPadding(dp(5.33f), 0, dp(5.33f), 0); + priceView.setBackground(Theme.createRoundRectDrawable(dp(16), 0xFFEEB402)); + FrameLayout backgroundLayout = new FrameLayout(context); + backgroundLayout.setBackground(Theme.createRoundRectDrawable(dp(20), Theme.getColor(Theme.key_dialogBackground, resourcesProvider))); + backgroundLayout.setPadding(dp(1.33f), dp(1.33f), dp(1.33f), dp(1.33f)); + backgroundLayout.addView(priceView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 16, Gravity.FILL)); + imageViewLayout.addView(backgroundLayout, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 16+2.66f, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL)); } StarsBalanceView balanceView = new StarsBalanceView(context, currentAccount); @@ -1892,9 +1961,32 @@ protected void onDetachedFromWindow() { titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); titleView.setTypeface(AndroidUtilities.bold()); titleView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); - titleView.setText(getString(R.string.StarsConfirmPurchaseTitle)); + if (subscription_period > 0) { + titleView.setText(Emoji.replaceEmoji(photo != null ? purchase : getString(R.string.StarsConfirmSubscriptionTitle), titleView.getPaint().getFontMetricsInt(), false)); + } else { + titleView.setText(Emoji.replaceEmoji(photo != null ? purchase : getString(R.string.StarsConfirmPurchaseTitle), titleView.getPaint().getFontMetricsInt(), false)); + } + NotificationCenter.listenEmojiLoading(titleView); titleView.setGravity(Gravity.CENTER); - linearLayout.addView(titleView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 0, 8, 0, 0)); + linearLayout.addView(titleView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 0, photo != null ? -8 : 8, 0, 0)); + + if (photo != null) { + LinearLayout chipLayout = new LinearLayout(context); + chipLayout.setOrientation(LinearLayout.HORIZONTAL); + chipLayout.setBackground(Theme.createRoundRectDrawable(dp(28), Theme.getColor(Theme.key_windowBackgroundGray, resourcesProvider))); + BackupImageView imageView = new BackupImageView(context); + imageView.setRoundRadius(dp(14)); + AvatarDrawable avatarDrawable = new AvatarDrawable(); + avatarDrawable.setInfo(user); + imageView.setForUserOrChat(user, avatarDrawable); + chipLayout.addView(imageView, LayoutHelper.createLinear(28, 28)); + TextView textView = new TextView(context); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); + textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + textView.setText(UserObject.getUserName(user)); + chipLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, 6, 0, 10, 0)); + linearLayout.addView(chipLayout, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 28, Gravity.CENTER_HORIZONTAL, 0, 8, 0, 2)); + } TextView subtitleView = new TextView(context); subtitleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); @@ -1948,16 +2040,34 @@ protected void onDetachedFromWindow() { } subtitleView.setText(AndroidUtilities.replaceTags(c)); } else { - subtitleView.setText(AndroidUtilities.replaceTags(formatPluralStringComma("StarsConfirmPurchaseText", (int) stars, purchase, UserObject.getUserName(user)))); + if (subscription_period > 0) { + subtitleView.setText(AndroidUtilities.replaceTags(formatPluralStringComma("StarsConfirmSubscriptionText2", (int) stars, purchase, UserObject.getUserName(user)))); + } else { + subtitleView.setText(AndroidUtilities.replaceTags(formatPluralStringComma("StarsConfirmPurchaseText2", (int) stars, purchase, UserObject.getUserName(user)))); + } } subtitleView.setMaxWidth(HintView2.cutInFancyHalf(subtitleView.getText(), subtitleView.getPaint())); subtitleView.setGravity(Gravity.CENTER); - linearLayout.addView(subtitleView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 0, 6, 0, 24)); + linearLayout.addView(subtitleView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 0, 6, 0, 18)); ButtonWithCounterView button = new ButtonWithCounterView(context, resourcesProvider); - button.setText(replaceStars(AndroidUtilities.replaceTags(formatPluralStringComma("StarsConfirmPurchaseButton", (int) stars))), false); + if (subscription_period > 0) { + button.setText(replaceStars(AndroidUtilities.replaceTags(formatPluralStringComma("StarsConfirmSubscriptionButton", (int) stars))), false); + } else { + button.setText(replaceStars(AndroidUtilities.replaceTags(formatPluralStringComma("StarsConfirmPurchaseButton", (int) stars))), false); + } linearLayout.addView(button, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48)); + LinkSpanDrawable.LinksTextView footerTextView = new LinkSpanDrawable.LinksTextView(context, resourcesProvider); + footerTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider)); + footerTextView.setLinkTextColor(Theme.getColor(Theme.key_chat_messageLinkIn, resourcesProvider)); + footerTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + footerTextView.setText(AndroidUtilities.replaceSingleTag(getString(subscription_period > 0 ? R.string.StarsConfirmSubscriptionTOS : R.string.StarsConfirmPurchaseTOS), () -> { + Browser.openUrl(context, getString(R.string.StarsTOSLink)); + })); + footerTextView.setGravity(Gravity.CENTER); + linearLayout.addView(footerTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 12, 0, 2)); + b.setCustomView(linearLayout); BottomSheet sheet = b.create(); @@ -2356,10 +2466,13 @@ public void dismissInternal() { public static final int TYPE_BOT = 0; public static final int TYPE_SUBSCRIPTION_BUY = 1; public static final int TYPE_SUBSCRIPTION_KEEP = 2; - public static final int TYPE_SUBSCRIPTION_REFULFIL = 3; + public static final int TYPE_SUBSCRIPTION_REFULFILL = 3; public static final int TYPE_LINK = 4; public static final int TYPE_REACTIONS = 5; public static final int TYPE_STAR_GIFT_BUY = 6; + public static final int TYPE_BOT_SUBSCRIPTION_KEEP = 7; + public static final int TYPE_BIZ_SUBSCRIPTION_KEEP = 8; + public static final int TYPE_BIZ = 9; public StarsNeededSheet( Context context, @@ -2400,7 +2513,11 @@ public StarsNeededSheet( stringRes = "StarsNeededTextBuySubscription"; } else if (type == TYPE_SUBSCRIPTION_KEEP) { stringRes = "StarsNeededTextKeepSubscription"; - } else if (type == TYPE_SUBSCRIPTION_REFULFIL) { + } else if (type == TYPE_BOT_SUBSCRIPTION_KEEP) { + stringRes = "StarsNeededTextKeepBotSubscription"; + } else if (type == TYPE_BIZ_SUBSCRIPTION_KEEP) { + stringRes = "StarsNeededTextKeepBizSubscription"; + } else if (type == TYPE_SUBSCRIPTION_REFULFILL) { stringRes = "StarsNeededTextKeepSubscription"; } else if (type == TYPE_LINK) { stringRes = botName == null ? "StarsNeededTextLink" : "StarsNeededTextLink_" + botName.toLowerCase(); @@ -2411,6 +2528,8 @@ public StarsNeededSheet( stringRes = "StarsNeededTextReactions"; } else if (type == TYPE_STAR_GIFT_BUY) { stringRes = "StarsNeededTextGift"; + } else if (type == TYPE_BIZ) { + stringRes = "StarsNeededBizText"; } else { stringRes = "StarsNeededText"; } @@ -2949,6 +3068,9 @@ public static SpannableStringBuilder replaceStarsWithPlain(CharSequence cs, floa } public static CharSequence getTransactionTitle(int currentAccount, boolean bot, TL_stars.StarsTransaction t) { + if (t.floodskip) { + return LocaleController.getString(R.string.StarsTransactionFloodskip); + } if (!t.extended_media.isEmpty()) { return getString(R.string.StarMediaPurchase); } @@ -3277,6 +3399,8 @@ public boolean validateGroupId(long groupId) { platform = "fragment"; } else if (transaction.peer instanceof TL_stars.TL_starsTransactionPeerAds) { platform = "ads"; + } else if (transaction.peer instanceof TL_stars.TL_starsTransactionPeerAPI) { + platform = "api"; } CombinedDrawable drawable = (CombinedDrawable) SessionCell.createDrawable(100, platform); drawable.setIconSize(dp(40), dp(40)); @@ -3351,8 +3475,8 @@ public boolean validateGroupId(long groupId) { TableView tableView = new TableView(context, resourcesProvider); if (transaction.stargift != null) { if (!transaction.refund) { - // final boolean out = t.stars > 0 ? R.string.Gift2TransactionConverted : R.string.Gift2TransactionSent final long did = DialogObject.getPeerDialogId(transaction.peer.peer); + final TLRPC.User didUser = MessagesController.getInstance(currentAccount).getUser(did); if (transaction.stars > 0) { // converted tableView.addRowUser(getString(R.string.StarGiveawayPrizeFrom), currentAccount, did, () -> { sheet[0].dismiss(); @@ -3364,6 +3488,8 @@ public boolean validateGroupId(long groupId) { lastFragment.presentFragment(ChatActivity.of(did)); } } + }, !UserObject.isDeleted(didUser) ? getString(R.string.Gift2ButtonSendGift) : null, () -> { + new GiftSheet(context, currentAccount, did, sheet[0]::dismiss).show(); }); tableView.addRowUser(getString(R.string.StarGiveawayPrizeTo), currentAccount, UserConfig.getInstance(currentAccount).getClientUserId(), () -> { sheet[0].dismiss(); @@ -3398,6 +3524,8 @@ public boolean validateGroupId(long groupId) { lastFragment.presentFragment(ChatActivity.of(did)); } } + }, !UserObject.isDeleted(didUser) ? getString(R.string.Gift2ButtonSendGift) : null, () -> { + new GiftSheet(context, currentAccount, did, sheet[0]::dismiss).show(); }); } } @@ -3584,7 +3712,7 @@ public void updateDrawState(@NonNull TextPaint ds) { idLayout.setPadding(dp(12.66f), dp(9.33f), dp(10.66f), dp(9.33f)); textView = new TextView(context); textView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MONO)); - textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 9); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, transaction.id.length() > 25 ? 9 : 10); textView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); textView.setMaxLines(4); textView.setSingleLine(false); @@ -3605,6 +3733,10 @@ public void updateDrawState(@NonNull TextPaint ds) { tableView.addRowUnpadded(getString(R.string.StarsTransactionID), idLayout); } + if (transaction.floodskip && transaction.floodskip_number > 0) { + tableView.addRow(getString(R.string.StarsTransactionFloodskipNumberName), LocaleController.formatPluralStringComma("StarsTransactionFloodskipNumber", transaction.floodskip_number)); + } + tableView.addRow(getString(R.string.StarsTransactionDate), LocaleController.formatString(R.string.formatDateAtTime, LocaleController.getInstance().getFormatterGiveawayCard().format(new Date(transaction.date * 1000L)), LocaleController.getInstance().getFormatterDay().format(new Date(transaction.date * 1000L)))); if (transaction.stargift != null) { if (transaction.stargift.limited) { @@ -3677,18 +3809,51 @@ public static BottomSheet showSubscriptionSheet(Context context, int currentAcco FrameLayout topView = new FrameLayout(context); linearLayout.addView(topView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL, 0, 0, 0, 10)); - BackupImageView imageView = new BackupImageView(context); - imageView.setRoundRadius(dp(50)); + final boolean[] maybeCloseAfterUpdate = new boolean[1]; + final NotificationCenter.NotificationCenterDelegate observer = new NotificationCenter.NotificationCenterDelegate() { + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.starSubscriptionsLoaded) { + if (maybeCloseAfterUpdate[0] && sheet[0] != null) { + sheet[0].dismiss(); + } + } + } + }; + NotificationCenter.getInstance(currentAccount).addObserver(observer, NotificationCenter.starSubscriptionsLoaded); final long did = DialogObject.getPeerDialogId(subscription.peer); - AvatarDrawable avatarDrawable = new AvatarDrawable(); + BackupImageView imageView = new BackupImageView(context); + final TLObject peerObject; + final String peerName; + final boolean bot, business; if (did >= 0) { - TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(did); - avatarDrawable.setInfo(user); - imageView.setForUserOrChat(user, avatarDrawable); + final TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(did); + peerObject = user; + peerName = UserObject.getUserName(user); + bot = UserObject.isBot(user); + business = !bot; } else { - TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-did); - avatarDrawable.setInfo(chat); - imageView.setForUserOrChat(chat, avatarDrawable); + final TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-did); + peerObject = chat; + peerName = chat == null ? "" : chat.title; + bot = false; + business = false; + } + if (subscription.photo != null) { + imageView.setRoundRadius(dp(21)); + imageView.setImage(ImageLocation.getForWebFile(WebFile.createWithWebDocument(subscription.photo)), "100_100", null, 0, null); + } else { + imageView.setRoundRadius(dp(50)); + AvatarDrawable avatarDrawable = new AvatarDrawable(); + if (did >= 0) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(did); + avatarDrawable.setInfo(user); + imageView.setForUserOrChat(user, avatarDrawable); + } else { + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-did); + avatarDrawable.setInfo(chat); + imageView.setForUserOrChat(chat, avatarDrawable); + } } topView.addView(imageView, LayoutHelper.createFrame(100, 100, Gravity.CENTER)); @@ -3696,26 +3861,32 @@ public static BottomSheet showSubscriptionSheet(Context context, int currentAcco starBg.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogBackground, resourcesProvider), PorterDuff.Mode.SRC_IN)); Drawable starFg = context.getResources().getDrawable(R.drawable.star_small_inner); - ImageView starBgView = new ImageView(context); - starBgView.setImageDrawable(starBg); - topView.addView(starBgView, LayoutHelper.createFrame(28, 28, Gravity.CENTER)); - starBgView.setTranslationX(dp(34)); - starBgView.setTranslationY(dp(35)); - starBgView.setScaleX(1.1f); - starBgView.setScaleY(1.1f); + if (subscription.photo == null) { + ImageView starBgView = new ImageView(context); + starBgView.setImageDrawable(starBg); + topView.addView(starBgView, LayoutHelper.createFrame(28, 28, Gravity.CENTER)); + starBgView.setTranslationX(dp(34)); + starBgView.setTranslationY(dp(35)); + starBgView.setScaleX(1.1f); + starBgView.setScaleY(1.1f); - ImageView starFgView = new ImageView(context); - starFgView.setImageDrawable(starFg); - topView.addView(starFgView, LayoutHelper.createFrame(28, 28, Gravity.CENTER)); - starFgView.setTranslationX(dp(34)); - starFgView.setTranslationY(dp(35)); + ImageView starFgView = new ImageView(context); + starFgView.setImageDrawable(starFg); + topView.addView(starFgView, LayoutHelper.createFrame(28, 28, Gravity.CENTER)); + starFgView.setTranslationX(dp(34)); + starFgView.setTranslationY(dp(35)); + } TextView textView = new TextView(context); textView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); textView.setTypeface(AndroidUtilities.bold()); textView.setGravity(Gravity.CENTER); - textView.setText(getString(R.string.StarsSubscriptionTitle)); + if (!TextUtils.isEmpty(subscription.title)) { + textView.setText(subscription.title); + } else { + textView.setText(getString(R.string.StarsSubscriptionTitle)); + } linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 20, 0, 20, 4)); textView = new TextView(context); @@ -3730,7 +3901,7 @@ public static BottomSheet showSubscriptionSheet(Context context, int currentAcco } linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 20, 0, 20, 4)); - TableView tableView = new TableView(context, resourcesProvider); + final TableView tableView = new TableView(context, resourcesProvider); textView = new LinkSpanDrawable.LinksTextView(context, resourcesProvider); textView.setPadding(dp(12.66f), dp(9.33f), dp(12.66f), dp(9.33f)); textView.setEllipsize(TextUtils.TruncateAt.END); @@ -3742,10 +3913,17 @@ public static BottomSheet showSubscriptionSheet(Context context, int currentAcco AvatarSpan avatarSpan = new AvatarSpan(textView, currentAccount, 24); CharSequence username; boolean deleted = false; - TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-did); - deleted = chat == null; - username = chat == null ? "" : chat.title; - avatarSpan.setChat(chat); + if (did >= 0) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(did); + deleted = user == null || UserObject.isDeleted(user); + username = UserObject.getPublicUsername(user); + avatarSpan.setUser(user); + } else { + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-did); + deleted = chat == null; + username = ChatObject.getPublicUsername(chat); + avatarSpan.setChat(chat); + } SpannableStringBuilder ssb = new SpannableStringBuilder("x " + username); ssb.setSpan(avatarSpan, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new ClickableSpan() { @@ -3765,7 +3943,11 @@ public void updateDrawState(@NonNull TextPaint ds) { }, 3, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); textView.setText(ssb); if (!deleted) { - tableView.addRowUnpadded(getString(R.string.StarsSubscriptionChannel), textView); + tableView.addRowUnpadded(getString(did < 0 ? R.string.StarsSubscriptionChannel : (business ? R.string.StarsSubscriptionBusiness : R.string.StarsSubscriptionBot)), textView); + } + + if (did >= 0 && !TextUtils.isEmpty(subscription.title)) { + tableView.addRow(getString(business ? R.string.StarsSubscriptionBusinessProduct : R.string.StarsSubscriptionBotProduct), subscription.title); } tableView.addRow( @@ -3774,7 +3956,7 @@ public void updateDrawState(@NonNull TextPaint ds) { ); final long now = ConnectionsManager.getInstance(currentAccount).getCurrentTime(); tableView.addRow( - getString(subscription.canceled ? R.string.StarsSubscriptionUntilExpires : now > subscription.until_date ? R.string.StarsSubscriptionUntilExpired : R.string.StarsSubscriptionUntilRenews), + getString(subscription.canceled || subscription.bot_canceled ? R.string.StarsSubscriptionUntilExpires : now > subscription.until_date ? R.string.StarsSubscriptionUntilExpired : R.string.StarsSubscriptionUntilRenews), LocaleController.formatString(R.string.formatDateAtTime, LocaleController.getInstance().getFormatterGiveawayCard().format(new Date(subscription.until_date * 1000L)), LocaleController.getInstance().getFormatterDay().format(new Date(subscription.until_date * 1000L))) ); linearLayout.addView(tableView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 17, 0, 0)); @@ -3795,19 +3977,19 @@ public void updateDrawState(@NonNull TextPaint ds) { textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider)); textView.setLinkTextColor(Theme.getColor(Theme.key_chat_messageLinkIn, resourcesProvider)); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - textView.setText(formatString(R.string.StarsSubscriptionRefulfillInfo, LocaleController.formatDateChat(subscription.until_date))); + textView.setText(formatString(bot ? R.string.StarsSubscriptionBotRefulfillInfo : R.string.StarsSubscriptionRefulfillInfo, LocaleController.formatDateChat(subscription.until_date))); textView.setSingleLine(false); textView.setMaxLines(4); textView.setGravity(Gravity.CENTER); linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 26, 7, 26, 15)); ButtonWithCounterView button = new ButtonWithCounterView(context, true, resourcesProvider); - button.setText(getString(R.string.StarsSubscriptionRefulfill), false); + button.setText(getString(bot ? R.string.StarsSubscriptionBotRefulfill : R.string.StarsSubscriptionRefulfill), false); linearLayout.addView(button, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); button.setOnClickListener(v -> { if (button.isLoading()) return; StarsController c = StarsController.getInstance(currentAccount); - final Runnable refulfil = () -> { + final Runnable refulfill = () -> { button.setLoading(true); TL_stars.TL_fulfillStarsSubscription req = new TL_stars.TL_fulfillStarsSubscription(); req.subscription_id = subscription.id; @@ -3825,11 +4007,21 @@ public void updateDrawState(@NonNull TextPaint ds) { })); }; if (c.balance < subscription.pricing.amount) { - new StarsNeededSheet(context, resourcesProvider, subscription.pricing.amount, StarsNeededSheet.TYPE_SUBSCRIPTION_KEEP, chat == null ? "" : chat.title, refulfil).show(); + new StarsNeededSheet(context, resourcesProvider, subscription.pricing.amount, business ? StarsNeededSheet.TYPE_BIZ_SUBSCRIPTION_KEEP : did < 0 ? StarsNeededSheet.TYPE_SUBSCRIPTION_KEEP : StarsNeededSheet.TYPE_BOT_SUBSCRIPTION_KEEP, peerName, refulfill).show(); } else { - refulfil.run(); + refulfill.run(); } }); + } else if (subscription.bot_canceled) { + textView = new LinkSpanDrawable.LinksTextView(context, resourcesProvider); + textView.setTextColor(Theme.getColor(Theme.key_color_red, resourcesProvider)); + textView.setLinkTextColor(Theme.getColor(Theme.key_chat_messageLinkIn, resourcesProvider)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + textView.setText(getString(business ? R.string.StarsSubscriptionBusinessCancelledText : R.string.StarsSubscriptionBotCancelledText)); + textView.setSingleLine(false); + textView.setMaxLines(4); + textView.setGravity(Gravity.CENTER); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 26, 7, 26, 15)); } else if (subscription.canceled) { textView = new LinkSpanDrawable.LinksTextView(context, resourcesProvider); textView.setTextColor(Theme.getColor(Theme.key_color_red, resourcesProvider)); @@ -3841,7 +4033,7 @@ public void updateDrawState(@NonNull TextPaint ds) { textView.setGravity(Gravity.CENTER); linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 26, 7, 26, 15)); - if (subscription.chat_invite_hash != null) { + if (subscription.chat_invite_hash != null || subscription.invoice_slug != null) { ButtonWithCounterView button = new ButtonWithCounterView(context, true, resourcesProvider); button.setText(getString(R.string.StarsSubscriptionRenew), false); linearLayout.addView(button, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); @@ -3861,7 +4053,7 @@ public void updateDrawState(@NonNull TextPaint ds) { BaseFragment fragment = LaunchActivity.getSafeLastFragment(); if (fragment != null) { - BulletinFactory.of(fragment).createUsersBulletin(Collections.singletonList(chat), getString(R.string.StarsSubscriptionRenewedToast), AndroidUtilities.replaceTags(formatString(R.string.StarsSubscriptionRenewedToastText, chat == null ? "" : chat.title))).show(false); + BulletinFactory.of(fragment).createUsersBulletin(Collections.singletonList(peerObject), getString(R.string.StarsSubscriptionRenewedToast), AndroidUtilities.replaceTags(formatString(R.string.StarsSubscriptionRenewedToastText, peerName))).show(false); } })); }); @@ -3897,7 +4089,15 @@ public void updateDrawState(@NonNull TextPaint ds) { BaseFragment fragment = LaunchActivity.getSafeLastFragment(); if (fragment != null) { - BulletinFactory.of(fragment).createUsersBulletin(Collections.singletonList(chat), getString(R.string.StarsSubscriptionCancelledToast), AndroidUtilities.replaceTags(formatString(R.string.StarsSubscriptionCancelledToastText, LocaleController.formatDateChat(subscription.until_date)))).show(false); + String message; + if (business && !TextUtils.isEmpty(subscription.title)) { + message = formatString(R.string.StarsSubscriptionCancelledBizToastText, LocaleController.formatDateChat(subscription.until_date), subscription.title); + } else if (bot && !TextUtils.isEmpty(subscription.title)) { + message = formatString(R.string.StarsSubscriptionCancelledBotToastText, LocaleController.formatDateChat(subscription.until_date), subscription.title); + } else { + message = formatString(R.string.StarsSubscriptionCancelledToastText, LocaleController.formatDateChat(subscription.until_date)); + } + BulletinFactory.of(fragment).createUsersBulletin(Collections.singletonList(peerObject), getString(R.string.StarsSubscriptionCancelledToast), AndroidUtilities.replaceTags(message)).show(false); } })); }); @@ -3913,45 +4113,55 @@ public void updateDrawState(@NonNull TextPaint ds) { textView.setGravity(Gravity.CENTER); linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 26, 7, 26, 15)); - if (subscription.chat_invite_hash != null) { + if (subscription.chat_invite_hash != null || subscription.invoice_slug != null) { ButtonWithCounterView button = new ButtonWithCounterView(context, true, resourcesProvider); button.setText(getString(R.string.StarsSubscriptionAgain), false); linearLayout.addView(button, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); button.setOnClickListener(v -> { if (button.isLoading()) return; button.setLoading(true); - TLRPC.TL_messages_checkChatInvite req = new TLRPC.TL_messages_checkChatInvite(); - req.hash = subscription.chat_invite_hash; - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { - button.setLoading(false); - if (res instanceof TLRPC.ChatInvite) { - TLRPC.ChatInvite invite = (TLRPC.ChatInvite) res; - if (invite.subscription_pricing == null) { // wtf - BulletinFactory.of(sheet[0].topBulletinContainer, resourcesProvider).createErrorBulletin(getString(R.string.UnknownError)).show(false); - return; - } - final long stars = invite.subscription_pricing.amount; - StarsController.getInstance(currentAccount).subscribeTo(req.hash, invite, (status, dialogId) -> { - if ("paid".equals(status) && dialogId != 0) { - AndroidUtilities.runOnUIThread(() -> { - BaseFragment lastFragment = LaunchActivity.getSafeLastFragment(); - if (lastFragment == null) return; - BaseFragment chatActivity = ChatActivity.of(dialogId); - lastFragment.presentFragment(chatActivity); - - TLRPC.Chat newChat = MessagesController.getInstance(currentAccount).getChat(-dialogId); - if (newChat != null) { - AndroidUtilities.runOnUIThread(() -> { - BulletinFactory.of(chatActivity).createSimpleBulletin(R.raw.stars_send, getString(R.string.StarsSubscriptionCompleted), AndroidUtilities.replaceTags(formatPluralString("StarsSubscriptionCompletedText", (int) stars, newChat.title))).show(true); - }, 250); - } - }); + if (subscription.chat_invite_hash != null) { + TLRPC.TL_messages_checkChatInvite req = new TLRPC.TL_messages_checkChatInvite(); + req.hash = subscription.chat_invite_hash; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + button.setLoading(false); + if (res instanceof TLRPC.ChatInvite) { + TLRPC.ChatInvite invite = (TLRPC.ChatInvite) res; + if (invite.subscription_pricing == null) { // wtf + BulletinFactory.of(sheet[0].topBulletinContainer, resourcesProvider).createErrorBulletin(getString(R.string.UnknownError)).show(false); + return; } - }); - } else { - BulletinFactory.of(sheet[0].topBulletinContainer, resourcesProvider).createErrorBulletin(LocaleController.getString(R.string.LinkHashExpired)).show(false); - } - })); + final long stars = invite.subscription_pricing.amount; + StarsController.getInstance(currentAccount).subscribeTo(req.hash, invite, (status, dialogId) -> { + if ("paid".equals(status) && dialogId != 0) { + AndroidUtilities.runOnUIThread(() -> { + BaseFragment lastFragment = LaunchActivity.getSafeLastFragment(); + if (lastFragment == null) return; + BaseFragment chatActivity = ChatActivity.of(dialogId); + lastFragment.presentFragment(chatActivity); + + TLRPC.Chat newChat = MessagesController.getInstance(currentAccount).getChat(-dialogId); + if (newChat != null) { + AndroidUtilities.runOnUIThread(() -> { + BulletinFactory.of(chatActivity).createSimpleBulletin(R.raw.stars_send, getString(R.string.StarsSubscriptionCompleted), AndroidUtilities.replaceTags(formatPluralString("StarsSubscriptionCompletedText", (int) stars, newChat.title))).show(true); + }, 250); + } + }); + } + }); + } else { + BulletinFactory.of(sheet[0].topBulletinContainer, resourcesProvider).createErrorBulletin(LocaleController.getString(R.string.LinkHashExpired)).show(false); + } + })); + } else if (subscription.invoice_slug != null) { + maybeCloseAfterUpdate[0] = true; + Browser.openUrl(context, Uri.parse("https://t.me/$" + subscription.invoice_slug), true, false, false, new Browser.Progress() { + @Override + public void end() { + button.setLoading(false); + } + }, null, false, true, false); + } }); } } @@ -3959,6 +4169,9 @@ public void updateDrawState(@NonNull TextPaint ds) { b.setCustomView(linearLayout); sheet[0] = b.create(); sheet[0].useBackgroundTopPadding = false; + sheet[0].setOnDismissListener(d -> { + NotificationCenter.getInstance(currentAccount).removeObserver(observer, NotificationCenter.starSubscriptionsLoaded); + }); sheet[0].fixNavigationBar(); BaseFragment fragment = LaunchActivity.getSafeLastFragment(); @@ -4346,6 +4559,7 @@ public static BottomSheet showActionGiftSheet(Context context, int currentAccoun linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 20, 0, 20, 4)); final TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + final boolean fromBot = UserObject.isBot(user); textView = new LinkSpanDrawable.LinksTextView(context, resourcesProvider); textView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); @@ -4357,7 +4571,9 @@ public static BottomSheet showActionGiftSheet(Context context, int currentAccoun final int within = MessagesController.getInstance(currentAccount).stargiftsConvertPeriodMax - (ConnectionsManager.getInstance(currentAccount).getCurrentTime() - date); final int withinDays = Math.max(1, within / (60 * 60 * 24)); textView.setText(TextUtils.concat( - AndroidUtilities.replaceTags(out ? + AndroidUtilities.replaceTags(fromBot ? ( + action.saved ? LocaleController.getString(R.string.Gift2Info2BotRemove) : LocaleController.getString(R.string.Gift2Info2BotKeep) + ) : out ? action.saved && !action.converted ? formatString(R.string.Gift2InfoOutPinned, UserObject.getForcedFirstName(user)) : formatPluralStringComma(action.converted ? "Gift2InfoOutConverted" : "Gift2InfoOut", (int) action.convert_stars, UserObject.getForcedFirstName(user)) : action.converted ? formatPluralStringComma("Gift2InfoConverted", (int) action.convert_stars) : formatPluralStringComma("Gift2Info", (int) action.convert_stars) ), @@ -4368,10 +4584,20 @@ public static BottomSheet showActionGiftSheet(Context context, int currentAccoun )); linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 5, 5, 5, 4)); + if (action.name_hidden) { + textView = new TextView(context); + textView.setTextColor(Theme.getColor(Theme.key_dialogTextGray2, resourcesProvider)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + textView.setGravity(Gravity.CENTER); + textView.setText(getString(R.string.Gift2SenderHidden)); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 20, 13, 20, 2)); + } + TableView tableView = new TableView(context, resourcesProvider); final long selfId = UserConfig.getInstance(currentAccount).getClientUserId(); final long fromId = out ? selfId : dialogId; final long toId = out ? dialogId : selfId; + final TLRPC.User fromUser = MessagesController.getInstance(currentAccount).getUser(fromId); if (fromId != selfId) { tableView.addRowUser(getString(R.string.Gift2From), currentAccount, fromId, () -> { sheet[0].dismiss(); @@ -4390,7 +4616,7 @@ public static BottomSheet showActionGiftSheet(Context context, int currentAccoun } lastFragment.presentFragment(new ProfileActivity(args)); } - }, getString(R.string.Gift2ButtonSendGift), () -> { + }, fromId != selfId && fromId != UserObject.ANONYMOUS && !UserObject.isDeleted(fromUser) && !fromBot ? getString(R.string.Gift2ButtonSendGift) : null, () -> { new GiftSheet(context, currentAccount, fromId, sheet[0]::dismiss).show(); }); } @@ -4412,12 +4638,13 @@ public static BottomSheet showActionGiftSheet(Context context, int currentAccoun } lastFragment.presentFragment(new ProfileActivity(args)); } - }, getString(R.string.Gift2ButtonSendGift), () -> { + }, null, () -> { new GiftSheet(context, currentAccount, toId, sheet[0]::dismiss).show(); }); } + tableView.addRowDateTime(getString(R.string.StarsTransactionDate), date); Runnable convert = null; - if (!out && !action.converted && within > 0) { + if (!out && !action.converted && action.convert_stars > 0 && within > 0) { convert = () -> { new AlertDialog.Builder(context, resourcesProvider) .setTitle(getString(R.string.Gift2ConvertTitle)) @@ -4476,9 +4703,45 @@ public static BottomSheet showActionGiftSheet(Context context, int currentAccoun .show(); }; } - tableView.addRow(getString(R.string.Gift2Value), TextUtils.concat(replaceStarsWithPlain("⭐️ " + LocaleController.formatNumber(gift.stars, ','), .8f), " ", convert == null ? "" : ButtonSpan.make(formatPluralStringComma("Gift2ButtonSell", (int) action.convert_stars), convert, resourcesProvider))); + tableView.addRow(getString(R.string.Gift2Value), replaceStarsWithPlain(TextUtils.concat("⭐️ " + LocaleController.formatNumber(gift.stars, ','), " ", convert == null ? "" : ButtonSpan.make(formatPluralStringComma("Gift2ButtonSell", (int) action.convert_stars), convert, resourcesProvider)), .8f)); + final ButtonWithCounterView button1 = new ButtonWithCounterView(context, resourcesProvider); + final Runnable toggleShow = () -> { + if (button1.isLoading()) return; + button1.setLoading(true); + + TL_stars.saveStarGift req = new TL_stars.saveStarGift(); + final boolean unsave = req.unsave = action.saved; + req.msg_id = msg_id; + req.user_id = MessagesController.getInstance(currentAccount).getInputUser(dialogId); + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + BaseFragment lastFragment = LaunchActivity.getSafeLastFragment(); + if (lastFragment == null) return; + if (res instanceof TLRPC.TL_boolTrue) { + sheet[0].dismiss(); + StarsController.getInstance(currentAccount).invalidateProfileGifts(selfId); + BulletinFactory.of(lastFragment) + .createEmojiBulletin( + gift.sticker, + LocaleController.getString(unsave ? R.string.Gift2MadePrivateTitle : R.string.Gift2MadePublicTitle), + AndroidUtilities.replaceSingleTag(LocaleController.getString(unsave ? R.string.Gift2MadePrivate : R.string.Gift2MadePublic), lastFragment instanceof ProfileActivity ? null : () -> { + final Bundle args = new Bundle(); + args.putLong("user_id", UserConfig.getInstance(currentAccount).getClientUserId()); + args.putBoolean("my_profile", true); + args.putBoolean("open_gifts", true); + final ProfileActivity profileActivity = new ProfileActivity(args); + lastFragment.presentFragment(profileActivity); + }) + ) + .show(true); + } else if (err != null) { + BulletinFactory.of(sheet[0].topBulletinContainer, resourcesProvider).createErrorBulletin(formatString(R.string.UnknownErrorCode, err.text)).show(false); + } + })); + }; + if (!out && !action.converted) { + tableView.addRow(getString(R.string.Gift2Visibility), getString(action.saved ? R.string.Gift2Visible : R.string.Gift2Invisible), getString(action.saved ? R.string.Gift2VisibleHide : R.string.Gift2InvisibleShow), toggleShow); + } - tableView.addRowDateTime(getString(R.string.StarsTransactionDate), date); if (gift.limited) { addAvailabilityRow(tableView, currentAccount, gift, resourcesProvider); } @@ -4510,72 +4773,18 @@ public static BottomSheet showActionGiftSheet(Context context, int currentAccoun linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 5, 6, 5, 16)); } - if (out || action.converted) { - ButtonWithCounterView button = new ButtonWithCounterView(context, resourcesProvider); - button.setText(getString(R.string.OK), false); - linearLayout.addView(button, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); +// if (out || action.converted) { + button1.setText(getString(R.string.OK), false); + linearLayout.addView(button1, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); b.setCustomView(linearLayout); sheet[0] = b.create(); sheet[0].useBackgroundTopPadding = false; - button.setOnClickListener(v -> { - sheet[0].dismiss(); - }); - - sheet[0].fixNavigationBar(); - BaseFragment fragment = LaunchActivity.getSafeLastFragment(); - if (!AndroidUtilities.isTablet() && !AndroidUtilities.hasDialogOnTop(fragment)) { - sheet[0].makeAttached(fragment); - } - sheet[0].show(); - return sheet[0]; - } else { - ButtonWithCounterView button1 = new ButtonWithCounterView(context, resourcesProvider); - button1.setText(getString(action.saved ? R.string.Gift2ProfileMakeInvisible : R.string.Gift2ProfileMakeVisible), false); - linearLayout.addView(button1, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, 0, 0, 0, 4)); - button1.setOnClickListener(v -> { - if (button1.isLoading()) return; - button1.setLoading(true); - - TL_stars.saveStarGift req = new TL_stars.saveStarGift(); - final boolean unsave = req.unsave = action.saved; - req.msg_id = msg_id; - req.user_id = MessagesController.getInstance(currentAccount).getInputUser(dialogId); - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { - BaseFragment lastFragment = LaunchActivity.getSafeLastFragment(); - if (lastFragment == null) return; - if (res instanceof TLRPC.TL_boolTrue) { - sheet[0].dismiss(); - StarsController.getInstance(currentAccount).invalidateProfileGifts(selfId); - BulletinFactory.of(lastFragment) - .createEmojiBulletin( - gift.sticker, - LocaleController.getString(unsave ? R.string.Gift2MadePrivateTitle : R.string.Gift2MadePublicTitle), - AndroidUtilities.replaceSingleTag(LocaleController.getString(unsave ? R.string.Gift2MadePrivate : R.string.Gift2MadePublic), lastFragment instanceof ProfileActivity ? null : () -> { - final Bundle args = new Bundle(); - args.putLong("user_id", UserConfig.getInstance(currentAccount).getClientUserId()); - args.putBoolean("my_profile", true); - args.putBoolean("open_gifts", true); - final ProfileActivity profileActivity = new ProfileActivity(args); - lastFragment.presentFragment(profileActivity); - }) - ) - .show(true); - } else if (err != null) { - BulletinFactory.of(sheet[0].topBulletinContainer, resourcesProvider).createErrorBulletin(formatString(R.string.UnknownErrorCode, err.text)).show(false); - } else { - BulletinFactory.of(sheet[0].topBulletinContainer, resourcesProvider).createErrorBulletin(getString(R.string.UnknownError)).show(false); - } - button1.setLoading(false); - })); + sheet[0].dismiss(); }); - b.setCustomView(linearLayout); - sheet[0] = b.create(); - sheet[0].useBackgroundTopPadding = false; - sheet[0].fixNavigationBar(); BaseFragment fragment = LaunchActivity.getSafeLastFragment(); if (!AndroidUtilities.isTablet() && !AndroidUtilities.hasDialogOnTop(fragment)) { @@ -4583,7 +4792,24 @@ public static BottomSheet showActionGiftSheet(Context context, int currentAccoun } sheet[0].show(); return sheet[0]; - } +// } else { +// button1.setText(getString(action.saved ? R.string.Gift2ProfileMakeInvisible : R.string.Gift2ProfileMakeVisible), false); +// linearLayout.addView(button1, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, 0, 0, 0, 4)); +// +// button1.setOnClickListener(v -> toggleShow.run()); +// +// b.setCustomView(linearLayout); +// sheet[0] = b.create(); +// sheet[0].useBackgroundTopPadding = false; +// +// sheet[0].fixNavigationBar(); +// BaseFragment fragment = LaunchActivity.getSafeLastFragment(); +// if (!AndroidUtilities.isTablet() && !AndroidUtilities.hasDialogOnTop(fragment)) { +// sheet[0].makeAttached(fragment); +// } +// sheet[0].show(); +// return sheet[0]; +// } } public static BottomSheet showGiftSheet(Context context, int currentAccount, long dialogId, boolean myProfile, TL_stars.UserStarGift userGift, Theme.ResourcesProvider resourcesProvider) { @@ -4594,6 +4820,9 @@ public static BottomSheet showGiftSheet(Context context, int currentAccount, lon if (gift == null) return null; + final TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + final boolean fromBot = UserObject.isBot(MessagesController.getInstance(currentAccount).getUser(userGift.from_id)); + BottomSheet.Builder b = new BottomSheet.Builder(context, false, resourcesProvider); BottomSheet[] sheet = new BottomSheet[1]; @@ -4615,15 +4844,15 @@ public static BottomSheet showGiftSheet(Context context, int currentAccount, lon textView.setText(getString(myProfile ? R.string.Gift2TitleReceived : R.string.Gift2TitleProfile)); linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 20, 0, 20, 9)); - textView = new TextView(context); - textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); - textView.setTypeface(AndroidUtilities.bold()); - textView.setGravity(Gravity.CENTER); - textView.setTextColor(Theme.getColor(Theme.key_color_green, resourcesProvider)); - textView.setText(replaceStarsWithPlain(LocaleController.formatNumber((int) Math.abs(Math.max(userGift.gift.convert_stars, userGift.convert_stars)), ' ') + " ⭐️", .8f)); - linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 20, 0, 20, 4)); - - final TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + if (!fromBot) { + textView = new TextView(context); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); + textView.setTypeface(AndroidUtilities.bold()); + textView.setGravity(Gravity.CENTER); + textView.setTextColor(Theme.getColor(Theme.key_color_green, resourcesProvider)); + textView.setText(replaceStarsWithPlain(LocaleController.formatNumber((int) Math.abs(Math.max(userGift.gift.convert_stars, userGift.convert_stars)), ' ') + " ⭐️", .8f)); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 20, 0, 20, 4)); + } int within = 0; if (myProfile) { @@ -4636,7 +4865,9 @@ public static BottomSheet showGiftSheet(Context context, int currentAccount, lon ((LinkSpanDrawable.LinksTextView) textView).setDisablePaddingsOffsetY(true); within = MessagesController.getInstance(currentAccount).stargiftsConvertPeriodMax - (ConnectionsManager.getInstance(currentAccount).getCurrentTime() - userGift.date); textView.setText(TextUtils.concat( - AndroidUtilities.replaceTags(myProfile ? + AndroidUtilities.replaceTags(fromBot ? ( + userGift.unsaved ? LocaleController.getString(R.string.Gift2Info2BotKeep) : LocaleController.getString(R.string.Gift2Info2BotRemove) + ) : myProfile ? within <= 0 ? formatPluralStringComma("Gift2Info2Expired", (int) userGift.convert_stars) : formatPluralStringComma("Gift2Info", (int) userGift.convert_stars) : formatPluralStringComma("Gift2Info2Out", (int) userGift.convert_stars, UserObject.getForcedFirstName(user)) ), @@ -4674,12 +4905,12 @@ public static BottomSheet showGiftSheet(Context context, int currentAccount, lon lastFragment.presentFragment(new ProfileActivity(args)); } } - }, fromId != selfId && fromId != UserObject.ANONYMOUS ? getString(R.string.Gift2ButtonSendGift) : null, () -> { + }, fromId != selfId && fromId != UserObject.ANONYMOUS && !fromBot && !UserObject.isDeleted(fromUser) ? getString(R.string.Gift2ButtonSendGift) : null, () -> { new GiftSheet(context, currentAccount, fromId, sheet[0]::dismiss).show(); }); tableView.addRow(getString(R.string.StarsTransactionDate), LocaleController.formatString(R.string.formatDateAtTime, LocaleController.getInstance().getFormatterGiveawayCard().format(new Date(userGift.date * 1000L)), LocaleController.getInstance().getFormatterDay().format(new Date(userGift.date * 1000L)))); Runnable convert = null; - if (myProfile && (userGift.flags & 8) != 0 && (userGift.flags & 2) != 0 && within > 0) { + if (myProfile && (userGift.flags & 8) != 0 && (userGift.flags & 16) != 0 && (userGift.flags & 2) != 0 && within > 0) { convert = () -> { new AlertDialog.Builder(context, resourcesProvider) .setTitle(getString(R.string.Gift2ConvertTitle)) @@ -4738,10 +4969,52 @@ public static BottomSheet showGiftSheet(Context context, int currentAccount, lon .show(); }; } - tableView.addRow(getString(R.string.Gift2Value), TextUtils.concat(replaceStarsWithPlain("⭐️ " + LocaleController.formatNumber(gift.stars, ','), .8f), " ", convert == null ? "" : ButtonSpan.make(formatPluralStringComma("Gift2ButtonSell", (int) userGift.convert_stars), convert, resourcesProvider))); + tableView.addRow(getString(R.string.Gift2Value), replaceStarsWithPlain(TextUtils.concat("⭐️ " + LocaleController.formatNumber(gift.stars, ','), " ", convert == null ? "" : ButtonSpan.make(formatPluralStringComma("Gift2ButtonSell", (int) userGift.convert_stars), convert, resourcesProvider)), .8f)); + if (gift.limited) { addAvailabilityRow(tableView, currentAccount, gift, resourcesProvider); } + final ButtonWithCounterView button1 = new ButtonWithCounterView(context, resourcesProvider); + final Runnable toggleShow = () -> { + if (button1.isLoading()) return; + button1.setLoading(true); + + TL_stars.saveStarGift req = new TL_stars.saveStarGift(); + final boolean unsave = req.unsave = !userGift.unsaved; + req.msg_id = userGift.msg_id; + req.user_id = MessagesController.getInstance(currentAccount).getInputUser(userGift.from_id); + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + BaseFragment lastFragment = LaunchActivity.getSafeLastFragment(); + if (lastFragment == null) return; + if (res instanceof TLRPC.TL_boolTrue) { + sheet[0].dismiss(); + StarsController.getInstance(currentAccount).invalidateProfileGifts(selfId); + BulletinFactory.of(lastFragment) + .createEmojiBulletin( + gift.sticker, + LocaleController.getString(unsave ? R.string.Gift2MadePrivateTitle : R.string.Gift2MadePublicTitle), + AndroidUtilities.replaceSingleTag(LocaleController.getString(unsave ? R.string.Gift2MadePrivate : R.string.Gift2MadePublic), lastFragment instanceof ProfileActivity ? null : () -> { + final Bundle args = new Bundle(); + args.putLong("user_id", UserConfig.getInstance(currentAccount).getClientUserId()); + args.putBoolean("my_profile", true); + args.putBoolean("open_gifts", true); + final ProfileActivity profileActivity = new ProfileActivity(args); + lastFragment.presentFragment(profileActivity); + }) + ) + .show(true); + } else if (err != null) { + BulletinFactory.of(sheet[0].topBulletinContainer, resourcesProvider).createErrorBulletin(formatString(R.string.UnknownErrorCode, err.text)).show(false); + } else { + BulletinFactory.of(sheet[0].topBulletinContainer, resourcesProvider).createErrorBulletin(getString(R.string.UnknownError)).show(false); + } + button1.setLoading(false); + })); + }; + if (myProfile && (userGift.flags & 8) != 0 && (userGift.flags & 2) != 0) { + tableView.addRow(getString(R.string.Gift2Visibility), getString(!userGift.unsaved ? R.string.Gift2Visible : R.string.Gift2Invisible), getString(!userGift.unsaved ? R.string.Gift2VisibleHide : R.string.Gift2InvisibleShow), toggleShow); + } + if (userGift.message != null && !TextUtils.isEmpty(userGift.message.text)) { tableView.addFullRow(userGift.message.text, userGift.message.entities); } @@ -4759,7 +5032,7 @@ public static BottomSheet showGiftSheet(Context context, int currentAccount, lon linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 5, 6, 5, 16)); } - if (!myProfile || (userGift.flags & 8) == 0 || (userGift.flags & 2) == 0) { +// if (!myProfile || (userGift.flags & 8) == 0 || (userGift.flags & 2) == 0) { ButtonWithCounterView button = new ButtonWithCounterView(context, resourcesProvider); button.setText(getString(R.string.OK), false); linearLayout.addView(button, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); @@ -4779,60 +5052,24 @@ public static BottomSheet showGiftSheet(Context context, int currentAccount, lon } sheet[0].show(); return sheet[0]; - } else { - ButtonWithCounterView button1 = new ButtonWithCounterView(context, resourcesProvider); - button1.setText(getString(!userGift.unsaved ? R.string.Gift2ProfileMakeInvisible : R.string.Gift2ProfileMakeVisible), false); - linearLayout.addView(button1, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, 0, 0, 0, 4)); - - button1.setOnClickListener(v -> { - if (button1.isLoading()) return; - button1.setLoading(true); - - TL_stars.saveStarGift req = new TL_stars.saveStarGift(); - final boolean unsave = req.unsave = !userGift.unsaved; - req.msg_id = userGift.msg_id; - req.user_id = MessagesController.getInstance(currentAccount).getInputUser(userGift.from_id); - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { - BaseFragment lastFragment = LaunchActivity.getSafeLastFragment(); - if (lastFragment == null) return; - if (res instanceof TLRPC.TL_boolTrue) { - sheet[0].dismiss(); - StarsController.getInstance(currentAccount).invalidateProfileGifts(selfId); - BulletinFactory.of(lastFragment) - .createEmojiBulletin( - gift.sticker, - LocaleController.getString(unsave ? R.string.Gift2MadePrivateTitle : R.string.Gift2MadePublicTitle), - AndroidUtilities.replaceSingleTag(LocaleController.getString(unsave ? R.string.Gift2MadePrivate : R.string.Gift2MadePublic), lastFragment instanceof ProfileActivity ? null : () -> { - final Bundle args = new Bundle(); - args.putLong("user_id", UserConfig.getInstance(currentAccount).getClientUserId()); - args.putBoolean("my_profile", true); - args.putBoolean("open_gifts", true); - final ProfileActivity profileActivity = new ProfileActivity(args); - lastFragment.presentFragment(profileActivity); - }) - ) - .show(true); - } else if (err != null) { - BulletinFactory.of(sheet[0].topBulletinContainer, resourcesProvider).createErrorBulletin(formatString(R.string.UnknownErrorCode, err.text)).show(false); - } else { - BulletinFactory.of(sheet[0].topBulletinContainer, resourcesProvider).createErrorBulletin(getString(R.string.UnknownError)).show(false); - } - button1.setLoading(false); - })); - }); - - b.setCustomView(linearLayout); - sheet[0] = b.create(); - sheet[0].useBackgroundTopPadding = false; - - sheet[0].fixNavigationBar(); - BaseFragment fragment = LaunchActivity.getSafeLastFragment(); - if (!AndroidUtilities.isTablet() && !AndroidUtilities.hasDialogOnTop(fragment)) { - sheet[0].makeAttached(fragment); - } - sheet[0].show(); - return sheet[0]; - } +// } else { +// button1.setText(getString(!userGift.unsaved ? R.string.Gift2ProfileMakeInvisible : R.string.Gift2ProfileMakeVisible), false); +// linearLayout.addView(button1, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, 0, 0, 0, 4)); +// +// button1.setOnClickListener(v -> toggleShow.run()); +// +// b.setCustomView(linearLayout); +// sheet[0] = b.create(); +// sheet[0].useBackgroundTopPadding = false; +// +// sheet[0].fixNavigationBar(); +// BaseFragment fragment = LaunchActivity.getSafeLastFragment(); +// if (!AndroidUtilities.isTablet() && !AndroidUtilities.hasDialogOnTop(fragment)) { +// sheet[0].makeAttached(fragment); +// } +// sheet[0].show(); +// return sheet[0]; +// } } public static BottomSheet showSoldOutGiftSheet(Context context, int currentAccount, TL_stars.StarGift gift, Theme.ResourcesProvider resourcesProvider) { @@ -4909,7 +5146,7 @@ public static void addAvailabilityRow(TableView tableView, int currentAccount, T final SpannableStringBuilder sb = new SpannableStringBuilder("x "); final LoadingSpan span = new LoadingSpan(rowTextView, dp(90), 0, resourcesProvider); span.setColors( - Theme.multAlpha(rowTextView.getPaint().getColor(), .65f), + Theme.multAlpha(rowTextView.getPaint().getColor(), .21f), Theme.multAlpha(rowTextView.getPaint().getColor(), .08f) ); sb.setSpan(span, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/StatisticActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/StatisticActivity.java index 9aae58f62b1..0ca3878eee6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/StatisticActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/StatisticActivity.java @@ -2208,11 +2208,11 @@ public static class ChartViewData { final int graphType; final String title; - boolean loading; - boolean isEmpty; - boolean isLanguages; - boolean useHourFormat; - boolean useWeekFormat; + public boolean loading; + public boolean isEmpty; + public boolean isLanguages; + public boolean useHourFormat; + public boolean useWeekFormat; public ChartViewData(String title, int grahType) { this.title = title; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/StickersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/StickersActivity.java index 8f23224c0be..4997883baa4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/StickersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/StickersActivity.java @@ -361,7 +361,7 @@ protected void calculateExtraLayoutSpace(@NonNull RecyclerView.State state, @Non inputStickerSets.add(inputStickerSetID); showDialog(new EmojiPacksAlert(StickersActivity.this, getParentActivity(), getResourceProvider(), inputStickerSets)); } else { - showDialog(new StickersAlert(getParentActivity(), StickersActivity.this, inputStickerSetID, null, null)); + showDialog(new StickersAlert(getParentActivity(), StickersActivity.this, inputStickerSetID, null, null, false)); } } else if (position == featuredStickersShowMoreRow || position == featuredRow) { if (currentType == MediaDataController.TYPE_EMOJIPACKS) { @@ -410,7 +410,7 @@ public void onStickerSetRemove(TLRPC.StickerSetCovered stickerSet) { inputs.add(inputId); showDialog(new EmojiPacksAlert(StickersActivity.this, getParentActivity(), getResourceProvider(), inputs)); } else { - showDialog(new StickersAlert(getParentActivity(), StickersActivity.this, null, stickerSet, null)); + showDialog(new StickersAlert(getParentActivity(), StickersActivity.this, null, stickerSet, null, false)); } } else { listAdapter.toggleSelected(position); 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 64725382e39..4fd546af774 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/PeerStoriesView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/PeerStoriesView.java @@ -131,6 +131,7 @@ import org.telegram.ui.Components.DotDividerSpan; import org.telegram.ui.Components.EditTextCaption; import org.telegram.ui.Components.EmojiPacksAlert; +import org.telegram.ui.Components.HashtagActivity; import org.telegram.ui.Components.HintView; import org.telegram.ui.Components.InstantCameraView; import org.telegram.ui.Components.LayoutHelper; @@ -833,11 +834,17 @@ public void onLinkClick(CharacterStyle span, View spoilersTextView) { } else if (span instanceof URLSpanNoUnderline) { String str = ((URLSpanNoUnderline) span).getURL(); if (str != null && (str.startsWith("#") || str.startsWith("$"))) { - Bundle args = new Bundle(); - args.putInt("type", MediaActivity.TYPE_STORIES_SEARCH); - args.putString("hashtag", str); - if (storyViewer != null) { - storyViewer.presentFragment(new MediaActivity(args, null)); + if (str.contains("@")) { + if (storyViewer != null) { + storyViewer.presentFragment(new HashtagActivity(str)); + } + } else { + Bundle args = new Bundle(); + args.putInt("type", MediaActivity.TYPE_STORIES_SEARCH); + args.putString("hashtag", str); + if (storyViewer != null) { + storyViewer.presentFragment(new MediaActivity(args, null)); + } } } else { String username = Browser.extractUsername(str); @@ -2694,7 +2701,7 @@ public void onTextChanged(CharSequence text, boolean bigChange, boolean fromDraf } if (mentionContainer.getAdapter() != null) { mentionContainer.setDialogId(dialogId); - mentionContainer.getAdapter().setUserOrChat(MessagesController.getInstance(currentAccount).getUser(dialogId), null); + mentionContainer.getAdapter().setUserOrChat(MessagesController.getInstance(currentAccount).getUser(dialogId), MessagesController.getInstance(currentAccount).getChat(-dialogId)); mentionContainer.getAdapter().searchUsernameOrHashtag(text, chatActivityEnterView.getCursorPosition(), null, false, false); } invalidate(); @@ -2877,6 +2884,10 @@ public void drawRoundRect(Canvas canvas, Rect rect, float radius) { canvas.drawRect(0, getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight() - 1, resourcesProvider.getPaint(Theme.key_paint_divider)); } } + @Override + protected boolean isStories() { + return true; + } }; mentionContainer.withDelegate(new MentionsContainerView.Delegate() { @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/PublicStoriesList.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/PublicStoriesList.java new file mode 100644 index 00000000000..ff4f7e86c69 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/PublicStoriesList.java @@ -0,0 +1,290 @@ +package org.telegram.ui.Stories; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.app.appsearch.SearchResult; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.style.ForegroundColorSpan; +import android.util.SparseArray; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.TextView; + +import org.checkerframework.checker.units.qual.A; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.HashtagSearchController; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.UserConfig; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ChatActivity; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.HashtagsSearchAdapter; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.SharedMediaLayout; + +import java.util.ArrayList; + +public class PublicStoriesList extends FrameLayout { + + private final Theme.ResourcesProvider resourcesProvider; + + private SharedMediaLayout sharedMediaLayout; + private String hashtag = ""; + private String username = null; + private boolean tabs = true; + + private final FrameLayout topView; + private final TextView topTitleView; + private final TextView textTitleView; + + public void setTabs(boolean tabs) { + if (this.tabs == tabs) return; + this.tabs = tabs; + requestLayout(); + } + + protected void onMessagesClick() { + + } + + public PublicStoriesList(BaseFragment fragment, Context context, Theme.ResourcesProvider resourcesProvider) { + super(context); + this.resourcesProvider = resourcesProvider; + + setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider)); + + setPadding(0, AndroidUtilities.statusBarHeight + ActionBar.getCurrentActionBarHeight() + dp(tabs ? 40 : 0), 0, dp(51)); + topView = new FrameLayout(context); + topView.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector, resourcesProvider), Theme.RIPPLE_MASK_ALL)); + topView.setOnClickListener(view -> { + onMessagesClick(); + }); + + topTitleView = new TextView(context); + topTitleView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + topTitleView.setTypeface(AndroidUtilities.bold()); + topTitleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + topView.addView(topTitleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL | Gravity.TOP, 13.66f, 6.66f, 13.66f, 0)); + + textTitleView = new TextView(context); + textTitleView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider)); + textTitleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); + topView.addView(textTitleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL | Gravity.TOP, 13.66f, 25, 13.66f, 0)); + + View shadow = new View(context); + shadow.setBackgroundColor(Theme.getColor(Theme.key_divider, resourcesProvider)); + topView.addView(shadow, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 1.0f / AndroidUtilities.density, Gravity.FILL_HORIZONTAL | Gravity.BOTTOM)); + + addView(topView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.FILL_HORIZONTAL | Gravity.TOP)); + + + + sharedMediaLayout = new SharedMediaLayout(context, 0, new SharedMediaLayout.SharedMediaPreloader(null), 0, null, null, null, SharedMediaLayout.TAB_STORIES, fragment, new SharedMediaLayout.Delegate() { + @Override + public void scrollToSharedMedia() { + + } + + @Override + public boolean onMemberClick(TLRPC.ChatParticipant participant, boolean b, boolean resultOnly, View vi) { + return false; + } + + @Override + public TLRPC.Chat getCurrentChat() { + return null; + } + + @Override + public boolean isFragmentOpened() { + return true; + } + + @Override + public RecyclerListView getListView() { + return null; + } + + @Override + public boolean canSearchMembers() { + return false; + } + + @Override + public void updateSelectedMediaTabText() { + // TODO + } + + }, SharedMediaLayout.VIEW_TYPE_MEDIA_ACTIVITY, resourcesProvider) { + @Override + protected void onSelectedTabChanged() { + super.onSelectedTabChanged(); + // TODO + } + + @Override + public String getStoriesHashtag() { + return hashtag; + } + + @Override + public String getStoriesHashtagUsername() { + return username; + } + + @Override + protected boolean canShowSearchItem() { + return false; + } + + @Override + protected void onSearchStateChanged(boolean expanded) { +// AndroidUtilities.removeAdjustResize(getParentActivity(), classGuid); +// AndroidUtilities.updateViewVisibilityAnimated(avatarContainer, !expanded, 0.95f, true); + } + + @Override + protected void drawBackgroundWithBlur(Canvas canvas, float y, Rect rectTmp2, Paint backgroundPaint) { +// fragmentView.drawBlurRect(canvas, getY() + y, rectTmp2, backgroundPaint, true); + } + + @Override + protected void invalidateBlur() { +// fragmentView.invalidateBlur(); + } + + @Override + protected boolean isStoriesView() { + return false; + } + + protected boolean customTabs() { + return true; + } + + @Override + protected boolean includeStories() { + return false; + } + + @Override + protected boolean includeSavedDialogs() { + return false; + } + + @Override + protected boolean isArchivedOnlyStoriesView() { + return false; + } + + @Override + protected int getInitialTab() { + return SharedMediaLayout.TAB_STORIES; + } + + private AnimatorSet actionModeAnimation; + + @Override + protected void showActionMode(boolean show) { + + } + + @Override + protected void onActionModeSelectedUpdate(SparseArray messageObjects) { + + } + + @Override + protected void onTabProgress(float progress) { + + } + + @Override + protected void onTabScroll(boolean scrolling) { + + } + + @Override + public boolean isSearchingStories() { + return true; + } + + @Override + public boolean addActionButtons() { + return false; + } + }; + if (sharedMediaLayout.getSearchOptionsItem() != null) { + sharedMediaLayout.getSearchOptionsItem().setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider), PorterDuff.Mode.MULTIPLY)); + } + sharedMediaLayout.setPinnedToTop(true); + sharedMediaLayout.photoVideoOptionsItem.setTranslationY(0); + if (sharedMediaLayout.getSearchOptionsItem() != null) { + sharedMediaLayout.getSearchOptionsItem().setTranslationY(0); + } + + addView(sharedMediaLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL, 0, 48, 0, 64)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setPadding(0, AndroidUtilities.statusBarHeight + ActionBar.getCurrentActionBarHeight() + dp(tabs ? 40 : 0), 0, dp(51)); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + public void setQuery(String username, String query) { + this.username = username; + this.hashtag = query; + setStoriesList(new StoriesController.SearchStoriesList(UserConfig.selectedAccount, username, query)); + updateTopView(); + } + + public void setStoriesList(StoriesController.StoriesList list) { + sharedMediaLayout.updateStoriesList(list); + list.load(true, 9, null); + updateTopView(); + } + + public void updateTopView() { + final HashtagSearchController.SearchResult searchResult = HashtagSearchController.getInstance(UserConfig.selectedAccount).getSearchResult(ChatActivity.SEARCH_PUBLIC_POSTS); + if (searchResult != null) { + String hashtag = searchResult.lastHashtag; + String username = null; + int index = hashtag.indexOf("@"); + if (index >= 0) { + username = hashtag.substring(index + 1); + hashtag = hashtag.substring(0, index); + } + if (username != null) { + SpannableStringBuilder title = new SpannableStringBuilder(searchResult.count + " messages in "); + int start = title.length(); + title.append("@").append(username); + title.setSpan(new ForegroundColorSpan(Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider)), start, title.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + topTitleView.setText(title); + } else { + topTitleView.setText(searchResult.count + " messages"); + } + textTitleView.setText("View posts with " + hashtag); + } + } + + +} 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 c4acf7a2df8..17c50d52514 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesController.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesController.java @@ -57,6 +57,7 @@ import org.telegram.ui.Components.Premium.LimitReachedBottomSheet; import org.telegram.ui.Components.Reactions.ReactionImageHolder; import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; +import org.telegram.ui.Components.Text; import org.telegram.ui.LaunchActivity; import org.telegram.ui.StatisticActivity; import org.telegram.ui.Stories.bots.BotPreviewsEditContainer; @@ -2448,6 +2449,8 @@ private static String storyItemMessageIds(List storyItems) { } } + public final ArrayList attachedSearchLists = new ArrayList<>(); + public void updateStoriesInLists(long dialogId, List storyItems) { FileLog.d("updateStoriesInLists " + dialogId + " storyItems[" + storyItems.size() + "] {" + storyItemIds(storyItems) + "}"); StoriesList pinned = getStoriesList(dialogId, StoriesList.TYPE_PINNED, false); @@ -2458,6 +2461,9 @@ public void updateStoriesInLists(long dialogId, List story if (archived != null) { archived.updateStories(storyItems); } + for (SearchStoriesList list : attachedSearchLists) { + list.updateStories(storyItems); + } } public void updateDeletedStoriesInLists(long dialogId, List storyItems) { @@ -2796,17 +2802,20 @@ public void delete(TLRPC.MessageMedia media) { public static class SearchStoriesList extends StoriesList { public final String query; + public final String username; public final TL_stories.MediaArea queryArea; - public SearchStoriesList(int currentAccount, String query) { + public SearchStoriesList(int currentAccount, String username, String query) { super(currentAccount, 0, TYPE_SEARCH, null); this.query = query; + this.username = username; this.queryArea = null; } public SearchStoriesList(int currentAccount, TL_stories.MediaArea area) { super(currentAccount, 0, TYPE_SEARCH, null); this.query = null; + this.username = null; this.queryArea = area; } @@ -2859,6 +2868,32 @@ public boolean load(boolean force, int count, List ids) { loading = true; + TLObject chat = null; + if (!TextUtils.isEmpty(username)) { + chat = MessagesController.getInstance(currentAccount).getUserOrChat(username); + if (chat == null) { + MessagesController.getInstance(currentAccount).getUserNameResolver().resolve(username, resolvedId -> { + TLObject resolvedChat = MessagesController.getInstance(currentAccount).getUserOrChat(username); + loading = false; + if (resolvedChat != null) { + load(force, count, ids); + } else { + this.count = 0; + last_offset = ""; + AndroidUtilities.cancelRunOnUIThread(super.notify); + AndroidUtilities.runOnUIThread(super.notify); + } + }); + return true; + } + } + if (chat != null) { + req.flags |= 4; + req.peer = MessagesController.getInputPeer(chat); +// if (req.hashtag.startsWith("#") || req.hashtag.startsWith("$")) +// req.hashtag = req.hashtag.substring(1); + } + this.reqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { this.reqId = 0; if (res instanceof TL_stories.TL_foundStories) { 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 a171efff48f..a949406f29d 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 @@ -89,6 +89,7 @@ public class CaptionContainerView extends FrameLayout { public FrameLayout limitTextContainer; public AnimatedTextView limitTextView; private int codePointCount; + private long dialogId; 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); @@ -265,7 +266,7 @@ public void onTextChanged(CharSequence text, int start, int before, int count) { createMentionsContainer(); } if (mentionContainer.getAdapter() != null) { -// mentionContainer.getAdapter().setUserOrChat(UserConfig.getInstance(currentAccount).getCurrentUser(), null); + mentionContainer.getAdapter().setUserOrChat(MessagesController.getInstance(currentAccount).getUser(dialogId), MessagesController.getInstance(currentAccount).getChat(-dialogId)); mentionContainer.getAdapter().searchUsernameOrHashtag(text, editText.getEditText().getSelectionStart(), null, false, false); } } @@ -343,6 +344,10 @@ public void afterTextChanged(Editable s) { fadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); } + public void setDialogId(long dialogId) { + this.dialogId = dialogId; + } + public int additionalRightMargin() { return 0; } @@ -457,16 +462,18 @@ public void drawRoundRect(Canvas canvas, Rect rectTmp, float radius) { } } } + @Override + protected boolean isStories() { + return true; + } }; mentionBackgroundBlur = new BlurringShader.StoryBlurDrawer(blurManager, mentionContainer, BlurringShader.StoryBlurDrawer.BLUR_TYPE_BACKGROUND); setupMentionContainer(); mentionContainer.withDelegate(new MentionsContainerView.Delegate() { - @Override public void replaceText(int start, int len, CharSequence replacingString, boolean allowShort) { replaceWithText(start, len, replacingString, allowShort); } - @Override public Paint.FontMetricsInt getFontMetrics() { return editText.getEditText().getPaint().getFontMetricsInt(); 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 a6911d5061a..f67ef08b216 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 @@ -2370,6 +2370,9 @@ public int getBottomOffset(int tag) { // privacySelector.setStoryPeriod(period); } }); + if (selectedDialogId != 0) { + captionEdit.setDialogId(selectedDialogId); + } captionEdit.setOnPremiumHint(this::showPremiumPeriodBulletin); captionEdit.setOnKeyboardOpen(open -> { if (open && timelineView != null) { @@ -6237,6 +6240,9 @@ public void setIconMuted(boolean muted, boolean animated) { public StoryRecorder selectedPeerId(long dialogId) { this.selectedDialogId = dialogId; + if (captionEdit != null) { + captionEdit.setDialogId(dialogId); + } return this; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/TopicCreateFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/TopicCreateFragment.java index d0a7711ce56..0d181f79b87 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/TopicCreateFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/TopicCreateFragment.java @@ -221,7 +221,7 @@ public void onItemClick(int id) { editForumRequest.title = topicName; editForumRequest.flags |= 1; } - if (topicForEdit.icon_emoji_id != editForumRequest.icon_emoji_id) { + if (topicForEdit.icon_emoji_id != selectedEmojiDocumentId) { editForumRequest.icon_emoji_id = selectedEmojiDocumentId; editForumRequest.flags |= 2; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/TopicsFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/TopicsFragment.java index 76bffc36bb7..f7157d87b0c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/TopicsFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/TopicsFragment.java @@ -89,7 +89,6 @@ import org.telegram.ui.Cells.GraySectionCell; import org.telegram.ui.Cells.HeaderCell; import org.telegram.ui.Cells.ProfileSearchCell; -import org.telegram.ui.Cells.TextCell; import org.telegram.ui.Cells.TopicSearchCell; import org.telegram.ui.Cells.UserCell; import org.telegram.ui.Components.AlertsCreator; @@ -241,10 +240,11 @@ public class TopicsFragment extends BaseFragment implements NotificationCenter.N private ActionBarMenuItem other; private MessagesSearchContainer searchContainer; public boolean searching; - private boolean opnendForSelect; - private boolean openedForForward; - private boolean openedForQuote; - private boolean openedForReply; + private final boolean openedForSelect; + private final boolean openedForForward; + private final boolean openedForQuote; + private final boolean openedForReply; + private final boolean openedForBotShare; private String voiceChatHash; private boolean openVideoChat; HashSet excludeTopics; @@ -292,8 +292,9 @@ public View getFullscreenView() { public TopicsFragment(Bundle bundle) { super(bundle); chatId = arguments.getLong("chat_id", 0); - opnendForSelect = arguments.getBoolean("for_select", false); + openedForSelect = arguments.getBoolean("for_select", false); openedForForward = arguments.getBoolean("forward_to", false); + openedForBotShare = arguments.getBoolean("bot_share_to", false); openedForQuote = arguments.getBoolean("quote", false); openedForReply = arguments.getBoolean("reply_to", false); voiceChatHash = arguments.getString("voicechat", null); @@ -801,12 +802,14 @@ public void onSearchFilterCleared(FiltersView.MediaFilterData filterData) { avatarContainer.setClipChildren(false); actionBar.addView(avatarContainer, 0, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, 56, 0, 86, 0)); - avatarContainer.getAvatarImageView().setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - openProfile(true); - } - }); + if (!openedForSelect) { + avatarContainer.getAvatarImageView().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + openProfile(true); + } + }); + } recyclerListView = new TopicsRecyclerView(context) { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { @@ -931,7 +934,7 @@ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { if (topic == null) { return; } - if (opnendForSelect) { + if (openedForSelect) { if (onTopicSelectedListener != null) { onTopicSelectedListener.onTopicSelected(topic); } @@ -959,7 +962,7 @@ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { ForumUtilities.openTopic(TopicsFragment.this, chatId, topic, 0); }); recyclerListView.setOnItemLongClickListener((view, position, x, y) -> { - if (opnendForSelect || getParentLayout() == null || getParentLayout().isInPreviewMode()) { + if (openedForSelect || getParentLayout() == null || getParentLayout().isInPreviewMode()) { return false; } if (!actionBar.isActionModeShowed() && !AndroidUtilities.isTablet() && view instanceof TopicDialogCell) { @@ -2094,7 +2097,7 @@ private void updateCreateTopicButton(boolean animated) { return; } TLRPC.Chat chatLocal = getMessagesController().getChat(chatId); - canShowCreateTopic = !ChatObject.isNotInChat(getMessagesController().getChat(chatId)) && ChatObject.canCreateTopic(chatLocal) && !searching && !opnendForSelect && !loadingTopics; + canShowCreateTopic = !ChatObject.isNotInChat(getMessagesController().getChat(chatId)) && ChatObject.canCreateTopic(chatLocal) && !searching && !openedForSelect && !loadingTopics; createTopicSubmenu.setVisibility(canShowCreateTopic ? View.VISIBLE : View.GONE); hideFloatingButton(!canShowCreateTopic, animated); } @@ -2462,7 +2465,7 @@ private void updateChatInfo(boolean forceAnimate) { boolean showReport = preferences.getBoolean("dialog_bar_report" + (-chatId), false); boolean showBlock = preferences.getBoolean("dialog_bar_block" + (-chatId), false); - if (!opnendForSelect) { + if (!openedForSelect) { if (chatLocal != null) { avatarContainer.setTitle(chatLocal.title); Drawable rightIcon = null; @@ -2477,6 +2480,8 @@ private void updateChatInfo(boolean forceAnimate) { avatarContainer.setTitle(LocaleController.getString(R.string.ReplyToDialog)); } else if (openedForQuote) { avatarContainer.setTitle(LocaleController.getString(R.string.QuoteTo)); + } else if (openedForBotShare) { + avatarContainer.setTitle(LocaleController.getString(R.string.BotShareToTopic)); } else if (openedForForward) { avatarContainer.setTitle(LocaleController.getString(R.string.ForwardTo)); } else { @@ -2500,7 +2505,7 @@ private void updateChatInfo(boolean forceAnimate) { AndroidUtilities.updateViewVisibilityAnimated(bottomOverlayProgress, false, 0.5f, animated); AndroidUtilities.updateViewVisibilityAnimated(bottomOverlayChatText, true, 0.5f, animated); setButtonType(BOTTOM_BUTTON_TYPE_JOIN); - } else if (chatLocal != null && !opnendForSelect && (ChatObject.isNotInChat(chatLocal) || getMessagesController().isJoiningChannel(chatLocal.id))) { + } else if (chatLocal != null && !openedForSelect && (ChatObject.isNotInChat(chatLocal) || getMessagesController().isJoiningChannel(chatLocal.id))) { bottomPannelVisibleLocal = true; boolean showProgress = false; @@ -2552,7 +2557,7 @@ public void onAnimationEnd(Animator animation) { } updateTopView(); - other.setVisibility(opnendForSelect ? View.GONE : View.VISIBLE); + other.setVisibility(openedForSelect ? View.GONE : View.VISIBLE); addMemberSubMenu.setVisibility(ChatObject.canAddUsers(chatLocal) ? View.VISIBLE : View.GONE); boostGroupSubmenu.setVisibility(ChatObject.isBoostSupported(chatLocal) && (getUserConfig().isPremium() || ChatObject.isBoosted(chatFull) || ChatObject.hasAdminRights(chatLocal)) ? View.VISIBLE : View.GONE); deleteChatSubmenu.setVisibility(chatLocal != null && !chatLocal.creator && !ChatObject.isNotInChat(chatLocal) ? View.VISIBLE : View.GONE); @@ -3923,7 +3928,7 @@ public void onTransitionAnimationEnd(boolean isOpen, boolean backward) { notificationsLocker.unlock(); if (!isOpen) { - if (opnendForSelect && removeFragmentOnTransitionEnd) { + if (openedForSelect && removeFragmentOnTransitionEnd) { removeSelfFromStack(); if (dialogsActivity != null) { dialogsActivity.removeSelfFromStack(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotAdView.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotAdView.java new file mode 100644 index 00000000000..a5a9c3360ce --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotAdView.java @@ -0,0 +1,237 @@ +package org.telegram.ui.bots; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.content.Context; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.net.Uri; +import android.text.Layout; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.StaticLayout; +import android.text.style.ForegroundColorSpan; +import android.text.style.URLSpan; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.Emoji; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.browser.Browser; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ChatActivity; +import org.telegram.ui.Components.AnimatedEmojiSpan; +import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.LinkSpanDrawable; +import org.telegram.ui.Components.ScaleStateListAnimator; +import org.telegram.ui.Stories.recorder.HintView2; + +public class BotAdView extends FrameLayout { + + private final LinearLayout layout; + private final Theme.ResourcesProvider resourcesProvider; + public final BackupImageView imageView; + public final ImageView closeView; + public final TextView titleView; + public final TextView channelTitleView; + public final TextView removeView; + public final LinkSpanDrawable.LinksTextView textView; + + public BotAdView(Context context, Theme.ResourcesProvider resourcesProvider) { + super(context); + this.resourcesProvider = resourcesProvider; + + layout = new LinearLayout(context); + layout.setOrientation(LinearLayout.HORIZONTAL); + layout.setPadding(dp(16), dp(5), dp(8), dp(5)); + ScaleStateListAnimator.apply(layout, .025f, 1.4f); + addView(layout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + + setBackground(Theme.createRadSelectorDrawable(Theme.multAlpha(Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider), .1f), 0, 0)); + + LinearLayout textLayout = new LinearLayout(context); + textLayout.setOrientation(LinearLayout.VERTICAL); + layout.addView(textLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 1, Gravity.LEFT)); + + LinearLayout titleLayout = new LinearLayout(context); + titleLayout.setOrientation(LinearLayout.HORIZONTAL); + textLayout.addView(titleLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0)); + + titleView = new TextView(context); + titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + titleView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + titleView.setTypeface(AndroidUtilities.bold()); + titleLayout.addView(titleView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 0, Gravity.CENTER_VERTICAL)); + NotificationCenter.listenEmojiLoading(titleView); + + removeView = new TextView(context); + removeView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 11); + removeView.setTextColor(Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider)); + ScaleStateListAnimator.apply(removeView, .1f, 1.5f); + removeView.setPadding(dp(6.33f), 0, dp(6.33f), 0); + removeView.setBackground(Theme.createRoundRectDrawable(dp(9), Theme.multAlpha(Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider), .10f))); + removeView.setText(LocaleController.getString(R.string.BotAdWhat)); + titleLayout.addView(removeView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 17, 0, Gravity.LEFT | Gravity.CENTER_VERTICAL, 5, 1, 0, 0)); + + channelTitleView = new TextView(context); + channelTitleView.setVisibility(View.GONE); + channelTitleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + channelTitleView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + channelTitleView.setTypeface(AndroidUtilities.bold()); + textLayout.addView(channelTitleView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, 2)); + NotificationCenter.listenEmojiLoading(channelTitleView); + + textView = new LinkSpanDrawable.LinksTextView(context); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); + textView.setLinkTextColor(Theme.getColor(Theme.key_chat_messageLinkIn, resourcesProvider)); + textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + textLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, 0)); + NotificationCenter.listenEmojiLoading(textView); + + imageView = new BackupImageView(context); + imageView.setRoundRadius(dp(4)); + imageView.setVisibility(View.GONE); + layout.addView(imageView, LayoutHelper.createLinear(48, 48, Gravity.RIGHT | Gravity.TOP, 10, 0, 2, 2)); + + closeView = new ImageView(context); + closeView.setBackground(Theme.createSelectorDrawable(Theme.RIPPLE_MASK_CIRCLE_AUTO, Theme.multAlpha(Theme.getColor(Theme.key_dialogEmptyImage, resourcesProvider), .20f))); + ScaleStateListAnimator.apply(closeView); + closeView.setImageResource(R.drawable.msg_close); + closeView.setScaleType(ImageView.ScaleType.CENTER); + closeView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogEmptyImage, resourcesProvider), PorterDuff.Mode.SRC_IN)); + closeView.setOnClickListener(v -> { + + }); + closeView.setVisibility(View.GONE); + layout.addView(closeView, LayoutHelper.createLinear(32, 32, Gravity.RIGHT | Gravity.TOP, 10, 3, 0, 2)); + } + + private boolean invalidatedMeasure = true; + public void set(ChatActivity chatActivity, MessageObject messageObject, Runnable onRemoveListener, Runnable onCloseListener) { + if (messageObject == null) return; + + invalidatedMeasure = true; + + CharSequence channel = messageObject.sponsoredTitle; + channel = Emoji.replaceEmoji(channel, titleView.getPaint().getFontMetricsInt(), false); + CharSequence text = messageObject.messageText; + text = Emoji.replaceEmoji(text, textView.getPaint().getFontMetricsInt(), false); + final String url = messageObject.sponsoredUrl; + + boolean hasMedia; + if (messageObject.sponsoredMedia != null) { + imageView.setVisibility(View.VISIBLE); + closeView.setVisibility(View.GONE); + if (messageObject.sponsoredMedia.document != null) { + TLRPC.PhotoSize thumbSize = FileLoader.getClosestPhotoSizeWithSize(messageObject.sponsoredMedia.document.thumbs, 48); + imageView.setImage( + ImageLocation.getForDocument(messageObject.sponsoredMedia.document), "48_48", + ImageLocation.getForDocument(thumbSize, messageObject.sponsoredMedia.document), "48_48", + null, 0, 0, null + ); + } else if (messageObject.sponsoredMedia.photo != null) { + TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(messageObject.sponsoredMedia.photo.sizes, 48, true, null, true); + TLRPC.PhotoSize thumbSize = FileLoader.getClosestPhotoSizeWithSize(messageObject.sponsoredMedia.photo.sizes, 48, true, photoSize, false); + imageView.setImage( + ImageLocation.getForPhoto(photoSize, messageObject.sponsoredMedia.photo), "48_48", + ImageLocation.getForPhoto(thumbSize, messageObject.sponsoredMedia.photo), "48_48", + null, 0, 0, null + ); + } + hasMedia = true; + } else if (messageObject.sponsoredPhoto != null) { + TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(messageObject.sponsoredPhoto.sizes, 48, true, null, true); + TLRPC.PhotoSize thumbSize = FileLoader.getClosestPhotoSizeWithSize(messageObject.sponsoredPhoto.sizes, 48, true, photoSize, false); + imageView.setImage( + ImageLocation.getForPhoto(photoSize, messageObject.sponsoredPhoto), "48_48", + ImageLocation.getForPhoto(thumbSize, messageObject.sponsoredPhoto), "48_48", + null, 0, 0, null + ); + imageView.setVisibility(View.VISIBLE); + closeView.setVisibility(View.GONE); + hasMedia = true; + } else { + imageView.setVisibility(View.GONE); + closeView.setVisibility(View.VISIBLE); + hasMedia = false; + } + + SpannableStringBuilder title = new SpannableStringBuilder(LocaleController.getString(R.string.SponsoredMessageAd)); + title.setSpan(new ForegroundColorSpan(Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider)), 0, title.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + title.append("  "); + title.append(channel); + if (titleView.getPaint().measureText(title.toString()) > AndroidUtilities.displaySize.x - dp(16 + 16 + 6.33f + 6.33f) - removeView.getPaint().measureText(removeView.getText().toString()) - dp(32) - dp(hasMedia ? 10 + 48 : 0)) { + title = new SpannableStringBuilder(LocaleController.getString(R.string.SponsoredMessageAd)); + title.setSpan(new ForegroundColorSpan(Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider)), 0, title.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + channelTitleView.setVisibility(View.VISIBLE); + channelTitleView.setText(channel); + } else { + channelTitleView.setVisibility(View.GONE); + } + titleView.setText(title); + textView.setText(text); + + setLayoutParams(LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM)); + + textView.setOnLinkPressListener(span -> { + if (chatActivity != null) { + chatActivity.logSponsoredClicked(messageObject, false, false); + } + if (span instanceof URLSpan) { + String spanUrl = ((URLSpan) span).getURL(); + if (spanUrl != null) spanUrl = spanUrl.trim(); + if (chatActivity != null && spanUrl != null && (spanUrl.startsWith("$") || spanUrl.startsWith("#"))) { + chatActivity.openHashtagSearch(spanUrl, true); + return; + } + } + span.onClick(textView); + }); + removeView.setOnClickListener(v -> { + if (onRemoveListener != null) { + onRemoveListener.run(); + } + }); + setOnClickListener(v -> { + if (chatActivity != null) { + chatActivity.logSponsoredClicked(messageObject, false, false); + } + Browser.openUrl(getContext(), Uri.parse(url), true, false, false, null, null, false, MessagesController.getInstance(UserConfig.selectedAccount).sponsoredLinksInappAllow, false); + }); + + closeView.setOnClickListener(v -> { + if (onCloseListener != null) { + onCloseListener.run(); + } + }); + } + + public int height() { + if (invalidatedMeasure || getMeasuredHeight() <= 0) { + measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.displaySize.x, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.displaySize.y, MeasureSpec.AT_MOST)); + } + return getMeasuredHeight(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + invalidatedMeasure = false; + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotBiometry.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotBiometry.java index 3b6a9306e9f..22ca68ea094 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotBiometry.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotBiometry.java @@ -8,10 +8,13 @@ import android.security.keystore.KeyProperties; import android.text.TextUtils; import android.util.Log; +import android.util.LongSparseArray; +import android.util.Pair; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; +import androidx.appcompat.view.WindowCallbackWrapper; import androidx.biometric.BiometricManager; import androidx.biometric.BiometricPrompt; import androidx.core.content.ContextCompat; @@ -47,6 +50,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import java.util.WeakHashMap; import java.util.concurrent.Executor; import javax.crypto.Cipher; @@ -69,7 +73,18 @@ public class BotBiometry { private String encrypted_token; private String iv; - public BotBiometry(Context context, int currentAccount, long botId) { + private final static WeakHashMap, BotBiometry> instances = new WeakHashMap<>(); + + public static BotBiometry get(Context context, int currentAccount, long botId) { + final Pair key = new Pair<>(currentAccount, botId); + BotBiometry instance = instances.get(key); + if (instance == null) { + instances.put(key, instance = new BotBiometry(context, currentAccount, botId)); + } + return instance; + } + + private BotBiometry(Context context, int currentAccount, long botId) { this.context = context; this.currentAccount = currentAccount; this.botId = botId; @@ -85,6 +100,20 @@ public void load() { this.disabled = prefs.getBoolean(botId + "_disabled", false); } + public boolean asked() { + return access_requested; + } + + public boolean granted() { + return access_granted; + } + + public void setGranted(boolean granted) { + this.access_requested = true; + this.access_granted = granted; + save(); + } + @Nullable public static String getAvailableType(Context context) { try { @@ -403,7 +432,7 @@ public static void getBots( final HashMap botEnabled = new HashMap<>(); for (long botId : botIds) { - final BotBiometry biometry = new BotBiometry(context, currentAccount, botId); + final BotBiometry biometry = BotBiometry.get(context, currentAccount, botId); if (!biometry.access_granted || !biometry.access_requested) continue; botEnabled.put(botId, !biometry.disabled); } @@ -471,6 +500,7 @@ public static void clear() { final SharedPreferences prefs = context.getSharedPreferences(PREF + i, Activity.MODE_PRIVATE); prefs.edit().clear().apply(); } + instances.clear(); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotDownloads.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotDownloads.java new file mode 100644 index 00000000000..fa7169ad5ae --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotDownloads.java @@ -0,0 +1,893 @@ +package org.telegram.ui.bots; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.app.Activity; +import android.app.DownloadManager; +import android.content.ContentResolver; +import android.content.Context; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Environment; +import android.os.LocaleList; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.TextUtils; +import android.util.Pair; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.webkit.MimeTypeMap; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.graphics.ColorUtils; + +import com.google.android.exoplayer2.util.MimeTypes; + +import org.json.JSONObject; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.Bulletin; +import org.telegram.ui.Components.BulletinFactory; +import org.telegram.ui.Components.CircularProgressDrawable; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.LoadingSpan; +import org.telegram.ui.Components.RLottieDrawable; +import org.telegram.ui.GradientClip; +import org.telegram.ui.LaunchActivity; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; + +public class BotDownloads { + + private static final String PREF = "botdownloads_"; + + public final Context context; + public final int currentAccount; + public final long botId; + public final DownloadManager downloadManager; + + private final ArrayList files = new ArrayList<>(); + private FileDownload currentFile; + + private final static HashMap, BotDownloads> instances = new HashMap<>(); + + public static BotDownloads get(Context context, int currentAccount, long botId) { + final Pair key = new Pair<>(currentAccount, botId); + BotDownloads instance = instances.get(key); + if (instance == null) { + instances.put(key, instance = new BotDownloads(context, currentAccount, botId)); + } + return instance; + } + + private BotDownloads(Context context, int currentAccount, long botId) { + this.context = context; + this.currentAccount = currentAccount; + this.botId = botId; + this.downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); + + final SharedPreferences prefs = context.getSharedPreferences(PREF + currentAccount, Activity.MODE_PRIVATE); + final Set jsons = prefs.getStringSet("" + botId, null); + if (jsons != null) { + for (String json : jsons) { + try { + final FileDownload file = new FileDownload(new JSONObject(json)); + if (file.file != null && file.file.exists()) { + files.add(file); + } + } catch (Exception e) { + FileLog.e(e); + } + } + } + } + + public FileDownload getCached(String url) { + for (FileDownload file : files) { + if (TextUtils.equals(file.url, url) && file.done) + return file; + } + return null; + } + + public FileDownload download(String url, String file_name) { + final FileDownload cached = getCached(url); + if (cached != null) { + currentFile = cached; + currentFile.resaved = true; + postNotify(); + return cached; + } + + final FileDownload file = new FileDownload(url, file_name); + currentFile = file; + file.shown = false; + files.add(file); + save(); + postNotify(); + + return file; + } + + public FileDownload getCurrent() { + return currentFile; + } + + public boolean isDownloading() { + for (FileDownload file : files) { + if (file.isDownloading()) + return true; + } + return false; + } + + public boolean hasFiles() { + return !files.isEmpty(); + } + + public ArrayList getFiles() { + return files; + } + + public void cancel(FileDownload file) { + if (file == null) return; + file.cancelled = true; + if (file.id != null) { + downloadManager.remove(file.id); + file.id = null; + } + files.remove(file); + postNotify(); + } + + public void save() { + final SharedPreferences prefs = context.getSharedPreferences(PREF + currentAccount, Activity.MODE_PRIVATE); + final SharedPreferences.Editor edit = prefs.edit(); + edit.clear(); + final HashSet set = new HashSet<>(); + for (FileDownload file : files) { + set.add(file.toJSON().toString()); + } + edit.putStringSet("" + botId, set); + edit.apply(); + } + + private void postNotify() { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.botDownloadsUpdate); + } + + public class FileDownload { + public Long id; + + public String url; + public String file_name; + public File file; + public String mime; + public long loaded_size; + public long size; + public boolean done; + public boolean cancelled; + + public long last_progress_time; + + public boolean resaved; + public boolean shown; + + public FileDownload(String url, String file_name) { + this.url = url; + this.file_name = file_name; + + final TLRPC.User bot = MessagesController.getInstance(currentAccount).getUser(botId); + + final DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)); + request.setTitle(UserObject.getUserName(bot)); + request.setDescription(TextUtils.isEmpty(file_name) ? "Downloading file..." : "Downloading " + file_name + "..."); + request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE); + request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, file_name); + + id = downloadManager.enqueue(request); + } + + public FileDownload(JSONObject json) { + url = json.optString("url"); + file_name = json.optString("file_name"); + size = json.optLong("size"); + done = json.optBoolean("done"); + mime = json.optString("mime"); + final String path = json.optString("path"); + if (!TextUtils.isEmpty(path)) + file = new File(path); + } + + public JSONObject toJSON() { + final JSONObject json = new JSONObject(); + try { + json.put("url", url); + json.put("file_name", file_name);; + json.put("size", size); + json.put("path", file == null ? null : file.getAbsolutePath()); + json.put("done", done); + json.put("mime", mime); + } catch (Exception e) { + FileLog.e(e); + } + return json; + } + + private final Runnable updateProgressRunnable = this::updateProgress; + private void updateProgress() { + if (done || cancelled) + return; + AndroidUtilities.cancelRunOnUIThread(updateProgressRunnable); + last_progress_time = System.currentTimeMillis(); + final DownloadManager.Query query = new DownloadManager.Query(); + query.setFilterById(id); + Cursor cursor = null; + try { + cursor = downloadManager.query(query); + if (cursor.moveToFirst()) { + int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)); + if (status == DownloadManager.STATUS_SUCCESSFUL) { + String localUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)); + this.file = new File(Uri.parse(localUri).getPath()); + done = true; + size = this.file.length(); + if (size <= 0) { + cancel(); + } + save(); + } else if (status == DownloadManager.STATUS_FAILED) { + cancel(); + return; + } else { + loaded_size = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); + size = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); + AndroidUtilities.runOnUIThread(updateProgressRunnable, 160L); + } + } else { + if (!done) { + cancel(); + } + } + } catch (Exception e) { + FileLog.e(e); + } finally { + if (cursor != null) { + cursor.close(); + } + } + postNotify(); + } + + public Pair getProgress() { + if (done) return new Pair<>(size, size); + if (id == null || cancelled) return new Pair<>(loaded_size, size); + if ((System.currentTimeMillis() - last_progress_time) < 150L) + return new Pair<>(loaded_size, size); + updateProgress(); + return new Pair<>(loaded_size, size); + } + + public void cancel() { + BotDownloads.this.cancel(this); + } + + public void open() { + if (file != null && file.exists()) { + AndroidUtilities.openForView(file, file.getName(), null, LaunchActivity.instance, null, true); + } + } + + public boolean isDownloading() { + return !done && id != null; + } + + public boolean isOver() { + return done || cancelled; + } + + public boolean isDownloaded() { + return done; + } + + } + + private static HashMap> cachedMimeAndSizes = new HashMap<>(); + public static void getMimeAndSize(final String url, Utilities.Callback2 whenDone) { + if (cachedMimeAndSizes.containsKey(url)) { + final Pair pair = cachedMimeAndSizes.get(url); + whenDone.run(pair.first, pair.second); + return; + } + new AsyncTask() { + + String mime; + long size; + + @Override + protected String doInBackground(String... strings) { + try { + + HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection(); + urlConnection.setRequestMethod("GET"); + urlConnection.setRequestProperty("Accept-Encoding", "identity"); + urlConnection.setConnectTimeout(1000); + urlConnection.setReadTimeout(1000); + urlConnection.setUseCaches(false); + urlConnection.setDefaultUseCaches(false); + urlConnection.setDoOutput(false); + urlConnection.setDoInput(false); + + urlConnection.getResponseCode(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + size = urlConnection.getContentLengthLong(); + } else { + size = urlConnection.getContentLength(); + } + mime = urlConnection.getContentType(); + if (mime.contains("; ")) + mime = mime.substring(0, mime.indexOf("; ")); + urlConnection.getInputStream().close(); + + return null; + } catch (Exception e) { + FileLog.e(e); + return null; + } + } + + @Override + protected void onPostExecute(String s) { + cachedMimeAndSizes.put(url, new Pair<>(mime, size)); + if (whenDone != null) { + whenDone.run(mime, size); + } + } + }.execute(url); + } + + public static void showAlert(Context context, String url, String file_name, String botname, Utilities.Callback whenDone) { + if (whenDone == null) return; + showAlert(context, url, file_name, botname, whenDone, 0, ""); +// +// final AlertDialog progressDialog = new AlertDialog(context, AlertDialog.ALERT_TYPE_SPINNER); +// progressDialog.showDelayed(300); +// getMimeAndSize(url, (mime, size) -> { +// progressDialog.dismiss(); +// showAlert(context, url, file_name, botname, whenDone, size, mime); +// }); + } + + public static AlertDialog showAlert(Context context, String url, String file_name, String botname, Utilities.Callback whenDone, final long size, final String mime) { + if (whenDone == null) return null; + + final AlertDialog.Builder b = new AlertDialog.Builder(context); + + b.setTitle(LocaleController.getString(R.string.BotDownloadFileTitle)); + b.setMessage(AndroidUtilities.replaceTags(LocaleController.formatString(R.string.BotDownloadFileText, botname))); + + final LinearLayout layout = new LinearLayout(context); + layout.setPadding(dp(22), 0, dp(22), 0); + layout.setOrientation(LinearLayout.HORIZONTAL); + + final ImageView imageView = new ImageView(context); + imageView.setScaleType(ImageView.ScaleType.CENTER); + imageView.setBackground(Theme.createCircleDrawable(dp(44), Theme.getColor(Theme.key_featuredStickers_addButton))); + imageView.setImageResource(R.drawable.msg_round_file_s); + layout.addView(imageView, LayoutHelper.createLinear(44, 44, Gravity.LEFT | Gravity.CENTER_VERTICAL, 0, 0, 10, 0)); + + final LinearLayout textLayout = new LinearLayout(context); + textLayout.setOrientation(LinearLayout.VERTICAL); + + final TextView titleView = new TextView(context); + titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); + titleView.setTypeface(AndroidUtilities.bold()); + titleView.setText(file_name); + titleView.setTextColor(Theme.getColor(Theme.key_dialogTextBlue2)); + textLayout.addView(titleView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, 3)); + + final AnimatedTextView subtitleView = new AnimatedTextView(context, true, true, true); + subtitleView.setTextSize(dp(12)); + final SpannableString ss = new SpannableString("l"); + final LoadingSpan loadingSpan = new LoadingSpan(subtitleView, dp(55)); + loadingSpan.setColors( + Theme.multAlpha(Theme.getColor(Theme.key_chat_inFileInfoText), .35f), + Theme.multAlpha(Theme.getColor(Theme.key_chat_inFileInfoText), .075f) + ); + ss.setSpan(loadingSpan, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + subtitleView.setText(ss); + getMimeAndSize(url, (_mime, _size) -> { + StringBuilder sb = new StringBuilder(); + if (_size > 0) { + sb.append("~").append(AndroidUtilities.formatFileSize(_size)); + } + final String ext = _mime == null ? null : getExt(_mime).toUpperCase(); + if (!TextUtils.isEmpty(ext)) { + if (sb.length() > 0) sb.append(" "); + sb.append(ext.toUpperCase()); + } + if (sb.length() <= 0) sb.append(LocaleController.getString(R.string.AttachDocument)); + subtitleView.setText(sb); + }); + subtitleView.setTextColor(Theme.getColor(Theme.key_chat_inFileInfoText)); + textLayout.addView(subtitleView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 15)); + + layout.addView(textLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL | Gravity.FILL_HORIZONTAL, 0, 0, 0, 2)); + + b.setView(layout); + + final boolean[] sent = new boolean[1]; + b.setNegativeButton(LocaleController.getString(R.string.Cancel), (di, w) -> { + if (!sent[0]) { + whenDone.run(false); + sent[0] = true; + } + }); + b.setPositiveButton(LocaleController.getString(R.string.BotDownloadFileDownload), (di, w) -> { + if (!sent[0]) { + whenDone.run(true); + sent[0] = true; + } + }); + + AlertDialog d = b.create(); + d.setOnDismissListener(di -> { + if (!sent[0]) { + whenDone.run(false); + sent[0] = true; + } + }); + d.show(); + + return d; + } + + public static String getExt(String mime) { + if (mime == null || mime.isEmpty()) return ""; + switch (mime) { + case "application/octet-stream": return "bin"; + case "application/x-abiword": return "abw"; + case "application/x-freearc": return "arc"; + case "video/x-msvideo": return "avi"; + case "application/vnd.amazon.ebook": return "azw"; + case "application/x-bzip": return "bz"; + case "application/x-bzip2": return "bz2"; + case "application/x-cdf": return "cda"; + case "application/x-csh": return "csh"; + case "application/msword": return "doc"; + case "application/vnd.openxmlformats-officedocument.wordprocessingml.document": return "docx"; + case "application/vnd.rar": return "rar"; + case "application/x-sh": return "sh"; + case "application/vnd.ms-fontobject": return "eot"; + case "application/epub+zip": return "epub"; + case "application/gzip": + case "application/x-gzip": return "gz"; + case "image/vnd.microsoft.icon": return "ico"; + case "application/java-archive": return "jar"; + case "text/calendar": return "ics"; + case "text/javascript": return "js"; + case "application/ld+json": return "jsonld"; + case "audio/x-midi": return "midi"; + case "audio/mpeg": return "mp3"; + case "application/vnd.apple.installer+xml": return "mpkg"; + case "application/vnd.oasis.opendocument.presentation": return "odp"; + case "application/vnd.oasis.opendocument.spreadsheet": return "ods"; + case "application/vnd.oasis.opendocument.text": return "odt"; + case "audio/ogg": return "opus"; + case "application/x-httpd-php": return "php"; + case "application/vnd.ms-powerpoint": return "ppt"; + case "application/vnd.openxmlformats-officedocument.presentationml.presentation": return "pptx"; + case "application/vnd.ms-excel": return "xls"; + case "text/plain": return "txt"; + case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": return "xlsx"; + case "video/3gpp": case "audio/3gpp": return "3gp"; + case "video/3gpp2": case "audio/3gpp2": return "3g2"; + case "application/x-7z-compressed": return "7z"; + default: + if (mime.contains("/")) + mime = mime.substring(mime.indexOf("/")+1); + if (mime.contains("-")) + mime = mime.substring(mime.indexOf("-")+1); + if (mime.contains("+")) + mime = mime.substring(0, mime.indexOf("+")); + return mime.toLowerCase(); + } + } + + public static class DownloadBulletin extends Bulletin.ButtonLayout { + + private final Theme.ResourcesProvider resourcesProvider; + public final BackgroundDrawable background; + public final StatusDrawable status; + + private final LinearLayout textLayout; + private final ImageView imageView; + private final TextView titleView; + private final TextView subtitleView; + + public DownloadBulletin(@NonNull Context context, Theme.ResourcesProvider resourcesProvider) { + super(context, resourcesProvider); + + this.resourcesProvider = resourcesProvider; + setBackground(background = new BackgroundDrawable(dp(10)).setColor(Theme.getColor(Theme.key_undo_background, resourcesProvider))); + + imageView = new ImageView(context); + imageView.setScaleType(ImageView.ScaleType.CENTER); + imageView.setImageDrawable(status = new StatusDrawable(context, imageView)); + addView(imageView, LayoutHelper.createFrame(40, 40, Gravity.CENTER_VERTICAL | Gravity.FILL_HORIZONTAL, 7, 0, 0, 0)); + + textLayout = new LinearLayout(context); + textLayout.setOrientation(LinearLayout.VERTICAL); + addView(textLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL | Gravity.FILL_HORIZONTAL, 54, 0, 0, 0)); + + titleView = new TextView(context); + titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + titleView.setTextColor(Theme.getColor(Theme.key_undo_infoColor, resourcesProvider)); + titleView.setTypeface(AndroidUtilities.bold()); + textLayout.addView(titleView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL | Gravity.TOP, 0, 0, 0, 2)); + + subtitleView = new TextView(context); + subtitleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); + subtitleView.setTextColor(Theme.getColor(Theme.key_undo_infoColor, resourcesProvider)); + textLayout.addView(subtitleView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL | Gravity.TOP, 0, 0, 0, 0)); + } + + private FileDownload file; + public boolean set(FileDownload file) { + if (this.file != file) { + status.reset(); + } + this.file = file; + titleView.setText(file.file_name); + if (file.isDownloading()) { + final Pair progress = file.getProgress(); + status.setProgress(progress); + if (progress.first <= 0) { + subtitleView.setText(LocaleController.getString(R.string.BotFileDownloading)); + } else if (progress.second <= 0) { + subtitleView.setText(AndroidUtilities.formatFileSize(progress.first)); + } else { + subtitleView.setText(AndroidUtilities.formatFileSize(progress.first) + " / " + AndroidUtilities.formatFileSize(progress.second)); + } + setButton(1); + } else if (file.cancelled) { + Bulletin b = getBulletin(); + if (b != null) { + b.hide(); + } + return true; + } else if (file.done) { + subtitleView.setText(LocaleController.getString(R.string.BotFileDownloaded)); + setButton(2); + status.setDone(false); + Bulletin b = getBulletin(); + if (b != null) { + b.setCanHide(false); + b.setDuration(Bulletin.DURATION_PROLONG); + b.setCanHide(true); + } + } + return false; + } + + private int currentButtonType = 0; + private void setButton(int type) { + if (currentButtonType == type) return; + currentButtonType = type; + if (type == 0) { + setButton(null); + } else if (type == 1) { + final Bulletin.UndoButton btn = new Bulletin.UndoButton(getContext(), true, resourcesProvider).setText(LocaleController.getString(R.string.BotFileDownloadCancel)).setUndoAction(() -> { + Bulletin b = getBulletin(); + if (b != null) { + b.setDuration(Bulletin.DURATION_LONG); + b.setCanHide(true); + } + if (file != null) { + file.cancel(); + } + }); + if (getBulletin() != null) { + btn.onAttach(this, getBulletin()); + } + setButton(btn); + } else if (type == 2) { + final Bulletin.UndoButton btn = new Bulletin.UndoButton(getContext(), true, resourcesProvider).setText(LocaleController.getString(R.string.BotFileDownloadOpen)).setUndoAction(() -> { + Bulletin b = getBulletin(); + if (b != null) { + b.hide(); + } + if (file != null) { + file.open(); + } + }); + if (getBulletin() != null) { + btn.onAttach(this, getBulletin()); + } + setButton(btn); + } + } + + public void setArrow(int rightMargin) { + background.setArrow(rightMargin); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(dp(8 + 52 + 8), MeasureSpec.EXACTLY)); + } + + private static class StatusDrawable extends Drawable { + + private final View view; + private final Paint strokePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final RectF rect = new RectF(); + private final long start; + + private final Drawable doc; + private boolean hasPercent; + private float progress; + private boolean done = false; + private boolean cancelled; + private AnimatedFloat animatedHasPercent = new AnimatedFloat(this::invalidateSelf, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + private AnimatedFloat animatedProgress = new AnimatedFloat(this::invalidateSelf, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + private AnimatedFloat animatedDone = new AnimatedFloat(this::invalidateSelf, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + + private RLottieDrawable doneDrawable; + + public StatusDrawable(Context context, View view) { + this.view = view; + start = System.currentTimeMillis(); + doc = context.getResources().getDrawable(R.drawable.search_files_filled).mutate(); + strokePaint.setStyle(Paint.Style.STROKE); + strokePaint.setStrokeWidth(dp(2)); + strokePaint.setStrokeCap(Paint.Cap.ROUND); + strokePaint.setStrokeJoin(Paint.Join.ROUND); + } + + public void reset() { + this.animatedDone.set(this.done = false, true); + this.cancelled = false; + if (doneDrawable != null) { + doneDrawable.recycle(true); + doneDrawable = null; + } + this.animatedHasPercent.set(this.hasPercent = false, true); + } + + public void setProgress(Pair progress) { + hasPercent = progress != null && progress.second > 0; + if (hasPercent) { + this.progress = Utilities.clamp((float) progress.first / progress.second, 1, 0); + } + invalidateSelf(); + } + + public void setDone(boolean cancelled) { + if (this.done) return; + this.done = true; + this.cancelled = cancelled; + doneDrawable = new RLottieDrawable(cancelled ? R.raw.error : R.raw.contact_check, cancelled ? "error" : "contact_check", dp(40), dp(40)); + doneDrawable.setMasterParent(view); + doneDrawable.setAllowDecodeSingleFrame(true); + doneDrawable.start(); + if (!cancelled) { + progress = 1.0f; + } + } + + @Override + public void draw(@NonNull Canvas canvas) { + final Rect bounds = getBounds(); + final int cx = bounds.centerX(), cy = bounds.centerY(); + + final float done = this.animatedDone.set(this.done); + + if (done < 1) { + final float s = .6f + .4f * (1.0f - done); + canvas.save(); + canvas.scale(s, s, cx, cy); + doc.setBounds( + cx - doc.getIntrinsicWidth() / 2, + cy - doc.getIntrinsicHeight() / 2, + cx + doc.getIntrinsicWidth() / 2, + cy + doc.getIntrinsicHeight() / 2 + ); + doc.setAlpha((int) (0xFF * (1.0f - done))); + doc.draw(canvas); + + final float r = dp(14); + strokePaint.setColor(Theme.multAlpha(0xFFFFFFFF, .20f * (1.0f - done))); + canvas.drawCircle(cx, cy, r, strokePaint); + strokePaint.setColor(Theme.multAlpha(0xFFFFFFFF, 1.0f * (1.0f - done))); + rect.set(cx - r, cy - r, cx + r, cy + r); + + final float hasPercent = this.animatedHasPercent.set(this.hasPercent); + + strokePaint.setColor(Theme.multAlpha(0xFFFFFFFF, .15f * (1.0f - done) * (1.0f - hasPercent))); + canvas.drawArc(rect, (-90 + -((-1.0f + ((System.currentTimeMillis() - start) % 600) / 600.0f) * 360)), -90.0f, false, strokePaint); + + float t = ((System.currentTimeMillis() - start) * .45f) % 5400; + float segment0 = Math.max(0, 1520 * t / 5400f - 20); + float segment1 = 1520 * t / 5400f; + for (int i = 0; i < 4; ++i) { + segment1 += CircularProgressDrawable.interpolator.getInterpolation((t - i * 1350) / 667f) * 250; + segment0 += CircularProgressDrawable.interpolator.getInterpolation((t - (667 + i * 1350)) / 667f) * 250; + } +// +//// float offset = 0, length = 0; +//// if (hasPercent < 1) { +//// offset += (-90 + -((-1.0f + ((System.currentTimeMillis() - start) % 600) / 600.0f) * 360)) * (1.0f - hasPercent); +//// length += -90.0f * (1.0f - hasPercent); +//// } +//// if (hasPercent > 0) { +//// offset += -90 * hasPercent; +//// length += -animatedProgress.set(progress) * 360 * hasPercent; +//// } + strokePaint.setColor(Theme.multAlpha(0xFFFFFFFF, 1.0f * (1.0f - done))); + canvas.drawArc(rect, -90 - segment0, -360 * Math.max(.02f, animatedProgress.set(progress)) * hasPercent, false, strokePaint); + invalidateSelf(); + canvas.restore(); + } + + if (done > 0) { + final float s = .6f + .4f * done; + if (cancelled) { + canvas.save(); + canvas.scale(s, s, cx, cy); + } + if (doneDrawable != null) { + doneDrawable.setBounds( + cx - doneDrawable.getIntrinsicWidth() / 2, + cy - doneDrawable.getIntrinsicHeight() / 2, + cx + doneDrawable.getIntrinsicWidth() / 2, + cy + doneDrawable.getIntrinsicHeight() / 2 + ); + doneDrawable.setAlpha((int) (0xFF * done)); + doneDrawable.draw(canvas); + } + + if (cancelled) { + canvas.restore(); + } + } + } + + @Override + public int getIntrinsicWidth() { + return dp(40); + } + @Override + public int getIntrinsicHeight() { + return dp(40); + } + + @Override + public void setAlpha(int alpha) {} + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) {} + @Override + public int getOpacity() { + return PixelFormat.TRANSPARENT; + } + } + + private static class BackgroundDrawable extends Drawable { + + private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final RectF rect = new RectF(); + private final int r; + private final Path path = new Path(); + + private boolean arrow; + private int arrowMargin; + private final AnimatedFloat arrowProgress = new AnimatedFloat(this::invalidateSelf, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat arrowX = new AnimatedFloat(this::invalidateSelf, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + + public BackgroundDrawable(int r) { + this.r = r; + path.moveTo(-dp(6.5f), 0); + path.lineTo(dp(6.5f), 0); + path.lineTo(0, -dp(6.16f)); + path.close(); + } + + public void setArrow(int rightMargin) { +// if (arrow == (rightMargin >= 0) && (!arrow || arrowMargin == rightMargin)) +// return; + arrow = rightMargin >= 0; + if (arrow) { + arrowMargin = rightMargin; + } + invalidateSelf(); + } + + public BackgroundDrawable setColor(int color) { + paint.setColor(color); + return this; + } + + @Override + public void draw(@NonNull Canvas canvas) { + rect.set(getBounds()); + rect.inset(dp(8), dp(8)); + canvas.drawRoundRect(rect, r, r, paint); + + final float arrowAlpha = this.arrowProgress.set(arrow); + final float arrowX = rect.right + dp(8) - this.arrowX.set(arrowMargin); + + if (arrowAlpha > 0) { + canvas.save(); + canvas.translate(arrowX, dp(8) + dp(6.16f) * (1.0f - arrowAlpha)); + canvas.drawPath(path, paint); + canvas.restore(); + } + } + + @Override + public void setAlpha(int alpha) {} + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) {} + @Override + public int getOpacity() { + return PixelFormat.TRANSPARENT; + } + } + + } + + public static void clear() { + Context context = ApplicationLoader.applicationContext; + if (context == null) return; + for (int i = 0; i < UserConfig.MAX_ACCOUNT_COUNT; ++i) { + final SharedPreferences prefs = context.getSharedPreferences(PREF + i, Activity.MODE_PRIVATE); + prefs.edit().clear().apply(); + } + instances.clear(); + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotLocation.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotLocation.java new file mode 100644 index 00000000000..f9996f328aa --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotLocation.java @@ -0,0 +1,568 @@ +package org.telegram.ui.bots; + +import static org.telegram.messenger.AndroidUtilities.calcBitmapColor; +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.LocaleController.getString; + +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.net.Uri; +import android.os.Build; +import android.util.Pair; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.checkerframework.checker.units.qual.A; +import org.json.JSONObject; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.DocumentObject; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.messenger.SvgHelper; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.INavigationLayout; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AlertsCreator; +import org.telegram.ui.Components.AttachableDrawable; +import org.telegram.ui.Components.AvatarDrawable; +import org.telegram.ui.Components.PermissionRequest; +import org.telegram.ui.Components.Text; +import org.telegram.ui.LaunchActivity; +import org.telegram.ui.ProfileActivity; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.WeakHashMap; + +public class BotLocation { + + public static final String PREF = "botlocation_"; + + public final Context context; + public final int currentAccount; + public final long botId; + + public boolean requested; + public boolean granted; + + private final static HashMap, BotLocation> instances = new HashMap<>(); + + public static BotLocation get(Context context, int currentAccount, long botId) { + final Pair key = new Pair<>(currentAccount, botId); + BotLocation instance = instances.get(key); + if (instance == null) { + instances.put(key, instance = new BotLocation(context, currentAccount, botId)); + } + return instance; + } + + private BotLocation(Context context, int currentAccount, long botId) { + this.context = context; + this.currentAccount = currentAccount; + this.botId = botId; + load(); + } + + public boolean asked() { + return requested; + } + + public boolean granted() { + return appHasPermission() && granted; + } + + public void setGranted(boolean granted, Runnable whenDone) { + this.requested = true; + if (granted && !appHasPermission()) { + final Activity activity = getActivity(); + if (activity == null) return; + final TLRPC.User bot = MessagesController.getInstance(currentAccount).getUser(botId); + final AlertDialog.Builder b = new AlertDialog.Builder(getActivity(), null); + b.setMessage(AndroidUtilities.replaceTags(LocaleController.formatString(R.string.BotLocationPermissionRequest, UserObject.getUserName(bot), UserObject.getUserName(bot)))); + b.setTopImage(new BotUserLocationDrawable(context, UserConfig.getInstance(currentAccount).getCurrentUser(), bot), Theme.getColor(Theme.key_dialogTopBackground)); + if (needToOpenSettings()) { + b.setPositiveButton(LocaleController.getString(R.string.BotLocationPermissionSettings), (di, w) -> { + try { + Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.parse("package:" + ApplicationLoader.applicationContext.getPackageName())); + activity.startActivity(intent); + } catch (Exception e) { + FileLog.e(e); + } + }); + } else { + b.setPositiveButton(LocaleController.getString(R.string.BotLocationPermissionAllow), (di, w) -> { + if (!appHasPermission()) { + PermissionRequest.requestPermissions(new String[] { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION }, _granted -> { + boolean someGranted = false; + for (int i = 0; i < _granted.length; ++i) { + if (_granted[i] == PackageManager.PERMISSION_GRANTED) { + someGranted = true; + } + } + this.requested = someGranted; + this.granted = someGranted; + this.save(); + for (Runnable listener : listeners) { + listener.run(); + } + if (whenDone != null) { + whenDone.run(); + } + }); + } else { + this.requested = true; + this.granted = true; + this.save(); + for (Runnable listener : listeners) { + listener.run(); + } + } + }); + } + b.setNegativeButton(LocaleController.getString(R.string.BotLocationPermissionDecline), (di, w) -> { + this.requested = true; + this.granted = false; + this.save(); + for (Runnable listener : listeners) { + listener.run(); + } + if (whenDone != null) { + whenDone.run(); + } + }); + b.show(); + } else { + this.granted = granted; + for (Runnable listener : listeners) { + listener.run(); + } + if (whenDone != null) { + whenDone.run(); + } + } + save(); + } + + private final HashSet listeners = new HashSet<>(); + public void listen(Runnable grantedChanged) { + listeners.add(grantedChanged); + } + public void unlisten(Runnable grantedChanged) { + listeners.remove(grantedChanged); + } + + public void load() { + SharedPreferences prefs = context.getSharedPreferences(PREF + currentAccount, Activity.MODE_PRIVATE); + requested = prefs.getBoolean(botId + "_requested", false); + granted = prefs.getBoolean(botId + "_granted", false); + if (granted && !appHasPermission()) { + granted = false; + requested = false; + save(); + for (Runnable listener : listeners) { + listener.run(); + } + } + } + + public void save() { + final SharedPreferences prefs = context.getSharedPreferences(PREF + currentAccount, Activity.MODE_PRIVATE); + final SharedPreferences.Editor edit = prefs.edit(); + edit.putBoolean(botId + "_granted", granted); + edit.putBoolean(botId + "_requested", requested); + edit.apply(); + } + + private Activity getActivity() { + Activity _activity = LaunchActivity.instance; + if (_activity == null) + _activity = AndroidUtilities.findActivity(context); + if (_activity == null) + _activity = AndroidUtilities.findActivity(ApplicationLoader.applicationContext); + return _activity; + } + + private boolean deviceHasLocation() { + return getActivity() != null && getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS); + } + + private boolean appHasPermission() { + final Activity activity = getActivity(); + return ( + Build.VERSION.SDK_INT < Build.VERSION_CODES.M || + activity != null && ( + activity.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED || + activity.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED + ) + ); + } + + private boolean needToOpenSettings() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return false; + final Activity activity = getActivity(); + if (activity == null) return false; + return ( + !activity.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_COARSE_LOCATION) || + !activity.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) + ); + } + + public void request(Utilities.Callback2 whenDone) { + final Activity activity = getActivity(); + if (activity == null) return; + + if (!deviceHasLocation()) { + if (whenDone != null) { + whenDone.run(false, false); + } + return; + } + + if (!appHasPermission() || !requested && !granted) { + final boolean[] sent = new boolean[1]; + final TLRPC.User bot = MessagesController.getInstance(currentAccount).getUser(botId); + final AlertDialog.Builder b = new AlertDialog.Builder(activity, null); + b.setMessage(AndroidUtilities.replaceTags(LocaleController.formatString(R.string.BotLocationPermissionRequest, UserObject.getUserName(bot), UserObject.getUserName(bot)))); + b.setTopImage(new BotUserLocationDrawable(context, UserConfig.getInstance(currentAccount).getCurrentUser(), bot), Theme.getColor(Theme.key_dialogTopBackground)); + if (!appHasPermission() && needToOpenSettings()) { + b.setPositiveButton(LocaleController.getString(R.string.BotLocationPermissionSettings), (di, w) -> { + try { + Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + intent.setData(Uri.parse("package:" + ApplicationLoader.applicationContext.getPackageName())); + activity.startActivity(intent); + } catch (Exception e) { + FileLog.e(e); + } + sent[0] = true; + if (whenDone != null) { + whenDone.run(false, false); + } + }); + } else { + b.setPositiveButton(LocaleController.getString(R.string.BotLocationPermissionAllow), (di, w) -> { + sent[0] = true; + if (!appHasPermission()) { + PermissionRequest.requestPermissions(new String[] { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION }, granted -> { + boolean someGranted = false; + for (int i = 0; i < granted.length; ++i) { + if (granted[i] == PackageManager.PERMISSION_GRANTED) { + someGranted = true; + } + } + this.requested = true; + this.granted = true; + this.save(); + for (Runnable listener : listeners) { + listener.run(); + } + if (whenDone != null) { + whenDone.run(true, someGranted); + } + }); + } else { + this.requested = true; + this.granted = true; + this.save(); + for (Runnable listener : listeners) { + listener.run(); + } + if (whenDone != null) { + whenDone.run(true, true); + } + } + }); + } + b.setNegativeButton(LocaleController.getString(R.string.BotLocationPermissionDecline), (di, w) -> { + if (sent[0]) return; + sent[0] = true; + this.requested = true; + this.granted = false; + this.save(); + for (Runnable listener : listeners) { + listener.run(); + } + if (whenDone != null) { + whenDone.run(true, false); + } + }); + b.setOnDismissListener((di) -> { + if (!sent[0]) { + this.requested = true; + this.granted = false; + this.save(); + for (Runnable listener : listeners) { + listener.run(); + } + sent[0] = true; + if (whenDone != null) { + whenDone.run(true, false); + } + } + }); + b.show(); + return; + } + + if (whenDone != null) { + whenDone.run(false, true); + } + } + + public JSONObject checkObject() { + JSONObject obj = new JSONObject(); + try { + obj.put("available", deviceHasLocation()); + if (deviceHasLocation()) { + obj.put("access_requested", requested); + if (requested) { + obj.put("access_granted", granted && appHasPermission()); + } + } + } catch (Exception e) { + FileLog.e(e); + } + return obj; + } + + public void requestObject(Utilities.Callback whenDone) { + if (whenDone == null) return; + + final JSONObject obj = new JSONObject(); + final boolean available = granted && appHasPermission() && deviceHasLocation(); + if (!available) { + try { + obj.put("available", false); + } catch (Exception e) { + FileLog.e(e); + } + whenDone.run(obj); + return; + } + + final LocationManager lm = (LocationManager) ApplicationLoader.applicationContext.getSystemService(Context.LOCATION_SERVICE); + List providers = lm.getProviders(true); + Location l = null; + for (int i = providers.size() - 1; i >= 0; i--) { + l = lm.getLastKnownLocation(providers.get(i)); + if (l != null) { + break; + } + } + + if (l == null && !lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) { + Context context = LaunchActivity.instance; + if (context == null) context = ApplicationLoader.applicationContext; + if (context != null) { + try { + final Context finalContext = context; + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTopAnimation(R.raw.permission_request_location, AlertsCreator.PERMISSIONS_REQUEST_TOP_ICON_SIZE, false, Theme.getColor(Theme.key_dialogTopBackground)); + builder.setMessage(getString(R.string.GpsDisabledAlertText)); + builder.setPositiveButton(getString(R.string.Enable), (dialog, id) -> { + try { + finalContext.startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS)); + } catch (Exception ignore) {} + }); + builder.setNegativeButton(getString(R.string.Cancel), null); + builder.show(); + } catch (Exception e) { + FileLog.e(e); + } + } + whenDone.run(locationObject(null)); + return; + } + + if (l != null) { + whenDone.run(locationObject(l)); + return; + } + + try { + final LocationListener[] listener = new LocationListener[1]; + listener[0] = new LocationListener() { + @Override + public void onLocationChanged(@NonNull Location location) { + lm.removeUpdates(listener[0]); + whenDone.run(locationObject(location)); + } + }; + lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1, 0, listener[0]); + } catch (Exception e) { + FileLog.e(e); + whenDone.run(locationObject(null)); + } + } + + private JSONObject locationObject(Location location) { + JSONObject obj = new JSONObject(); + try { + obj.put("available", location != null); + if (location == null) return obj; + + obj.put("latitude", location.getLatitude()); + obj.put("longitude", location.getLongitude()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + obj.put("horizontal_accuracy", location.getAccuracy()); + } else { + obj.put("horizontal_accuracy", null); + } + obj.put("altitude", location.getAltitude()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + obj.put("vertical_accuracy", location.getVerticalAccuracyMeters()); + } else { + obj.put("vertical_accuracy", null); + } + obj.put("course", location.getBearing()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + obj.put("course_accuracy", location.getBearingAccuracyDegrees()); + } else { + obj.put("course_accuracy", null); + } + obj.put("speed", location.getSpeed()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + obj.put("speed_accuracy", location.getSpeedAccuracyMetersPerSecond()); + } else { + obj.put("speed_accuracy", null); + } + } catch (Exception e) { + FileLog.e(e); + } + return obj; + } + + public static class BotUserLocationDrawable extends Drawable implements AttachableDrawable { + + private final Paint arrowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint whitePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final ImageReceiver userImageReceiver = new ImageReceiver(); + private final ImageReceiver botImageReceiver = new ImageReceiver(); + private final Drawable locationDrawable; + private final RectF rect = new RectF(); + + public BotUserLocationDrawable(Context context, TLRPC.User user, TLRPC.User bot) { + + arrowPaint.setColor(0xFFFFFFFF); + arrowPaint.setStyle(Paint.Style.STROKE); + arrowPaint.setStrokeWidth(dp(2)); + arrowPaint.setStrokeJoin(Paint.Join.ROUND); + arrowPaint.setStrokeCap(Paint.Cap.ROUND); + whitePaint.setColor(0xFFFFFFFF); + + locationDrawable = context.getResources().getDrawable(R.drawable.filled_location).mutate(); + locationDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogTopBackground), PorterDuff.Mode.SRC_IN)); + + AvatarDrawable avatarDrawable = new AvatarDrawable(); + avatarDrawable.setInfo(user); + userImageReceiver.setForUserOrChat(user, avatarDrawable); + userImageReceiver.setRoundRadius(dp(25)); + + avatarDrawable = new AvatarDrawable(); + avatarDrawable.setInfo(bot); + botImageReceiver.setForUserOrChat(bot, avatarDrawable); + botImageReceiver.setRoundRadius(dp(25)); + } + + @Override + public void onAttachedToWindow(ImageReceiver parent) { + userImageReceiver.onAttachedToWindow(); + botImageReceiver.onAttachedToWindow(); + } + + @Override + public void onDetachedFromWindow(ImageReceiver parent) { + userImageReceiver.onDetachedFromWindow(); + botImageReceiver.onDetachedFromWindow(); + } + + @Override + public void setParent(View view) { + botImageReceiver.setParentView(view); + userImageReceiver.setParentView(view); + } + + @Override + public void draw(@NonNull Canvas canvas) { + final Rect bounds = getBounds(); + + bgPaint.setColor(Theme.getColor(Theme.key_dialogTopBackground)); + + final float width = dp(50 + 36 + 50); + + userImageReceiver.setImageCoords(bounds.centerX() - width / 2f, bounds.centerY() - dp(25), dp(50), dp(50)); + userImageReceiver.draw(canvas); + + final float lcx = bounds.centerX() - width / 2f + dp(25 + 16), lcy = bounds.centerY() + dp(16); + canvas.drawCircle(lcx, lcy, dp(14), bgPaint); + canvas.drawCircle(lcx, lcy, dp(12), whitePaint); + locationDrawable.setBounds((int) (lcx - dp(9)), (int) (lcy - dp(9)), (int) (lcx + dp(9)), (int) (lcy + dp(9))); + locationDrawable.draw(canvas); + + canvas.drawLine(bounds.centerX() - dp(3.33f), bounds.centerY() - dp(7), bounds.centerX() + dp(3.33f), bounds.centerY(), arrowPaint); + canvas.drawLine(bounds.centerX() - dp(3.33f), bounds.centerY() + dp(7), bounds.centerX() + dp(3.33f), bounds.centerY(), arrowPaint); + + botImageReceiver.setImageCoords(bounds.centerX() + width / 2f - dp(50), bounds.centerY() - dp(25), dp(50), dp(50)); + botImageReceiver.draw(canvas); + } + + @Override + public void setAlpha(int alpha) { + + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSPARENT; + } + } + + public static void clear() { + Context context = ApplicationLoader.applicationContext; + if (context == null) return; + for (int i = 0; i < UserConfig.MAX_ACCOUNT_COUNT; ++i) { + final SharedPreferences prefs = context.getSharedPreferences(PREF + i, Activity.MODE_PRIVATE); + prefs.edit().clear().apply(); + } + instances.clear(); + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotSensors.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotSensors.java new file mode 100644 index 00000000000..95514273a04 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotSensors.java @@ -0,0 +1,463 @@ +package org.telegram.ui.bots; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.util.Log; + +import org.json.JSONObject; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.ui.web.BotWebViewContainer; + +public class BotSensors { + + private final SensorManager sensorManager; + + private Sensor accelerometer; + private long accelerometerDesiredRefreshRate; + private Sensor gyroscope; + private long gyroscopeDesiredRefreshRate; + private Sensor orientationMagnetometer; + private Sensor orientationAccelerometer; + private long absoluteOrientationDesiredRefreshRate; + private Sensor rotation; + private long relativeOrientationDesiredRefreshRate; + + public BotSensors(Context context, long bot_id) { + sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + } + + private BotWebViewContainer.MyWebView webView; + public void attachWebView(BotWebViewContainer.MyWebView webView) { + this.webView = webView; + } + public void detachWebView(BotWebViewContainer.MyWebView webView) { + if (this.webView == webView) { + this.webView = null; + pause(); + } + } + + public boolean startAccelerometer(long refresh_rate) { + if (sensorManager == null) return false; + if (accelerometer != null) return true; + accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + if (accelerometer == null) return false; + accelerometerDesiredRefreshRate = refresh_rate; + if (!paused) { + sensorManager.registerListener(accelerometerListener, accelerometer, getSensorDelay(refresh_rate)); + } + return true; + } + + public boolean stopAccelerometer() { + if (sensorManager == null) return false; + if (accelerometer == null) return true; + if (!paused) { + sensorManager.unregisterListener(accelerometerListener, accelerometer); + } + if (accelerometerListenerPostponed != null) { + AndroidUtilities.cancelRunOnUIThread(accelerometerListenerPostponed); + accelerometerListenerPostponed = null; + } + accelerometer = null; + return true; + } + + public boolean startGyroscope(long refresh_rate) { + if (sensorManager == null) return false; + if (gyroscope != null) return true; + gyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); + if (gyroscope == null) return false; + gyroscopeDesiredRefreshRate = refresh_rate; + if (!paused) { + sensorManager.registerListener(gyroscopeListener, gyroscope, getSensorDelay(refresh_rate)); + } + return true; + } + + public boolean stopGyroscope() { + if (sensorManager == null) return false; + if (gyroscope == null) return true; + if (!paused) { + sensorManager.unregisterListener(gyroscopeListener, gyroscope); + } + if (gyroscopeListenerPostponed != null) { + AndroidUtilities.cancelRunOnUIThread(gyroscopeListenerPostponed); + gyroscopeListenerPostponed = null; + } + gyroscope = null; + return true; + } + + public boolean startOrientation(boolean absolute, long refresh_rate) { + if (sensorManager == null) return false; + if (absolute) { + if (rotation != null) { + if (relativeOrientationListenerPostponed != null) { + AndroidUtilities.cancelRunOnUIThread(relativeOrientationListenerPostponed); + relativeOrientationListenerPostponed = null; + } + if (!paused) { + if (rotation != null) { + sensorManager.unregisterListener(relativeOrientationListener, rotation); + } + } + rotation = null; + } + if (orientationMagnetometer != null && orientationAccelerometer != null) return true; + orientationAccelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + orientationMagnetometer = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); + if (orientationAccelerometer == null || orientationMagnetometer == null) return false; + absoluteOrientationDesiredRefreshRate = refresh_rate; + if (!paused) { + sensorManager.registerListener(absoluteOrientationListener, orientationAccelerometer, getSensorDelay(refresh_rate)); + sensorManager.registerListener(absoluteOrientationListener, orientationMagnetometer, getSensorDelay(refresh_rate)); + } + } else { + if (orientationMagnetometer != null || orientationAccelerometer != null) { + if (absoluteOrientationListenerPostponed != null) { + AndroidUtilities.cancelRunOnUIThread(absoluteOrientationListenerPostponed); + absoluteOrientationListenerPostponed = null; + } + if (!paused) { + if (orientationAccelerometer != null) { + sensorManager.unregisterListener(absoluteOrientationListener, orientationAccelerometer); + } + if (orientationMagnetometer != null) { + sensorManager.unregisterListener(absoluteOrientationListener, orientationMagnetometer); + } + } + orientationAccelerometer = null; + orientationMagnetometer = null; + } + if (rotation != null) return true; + rotation = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR); + if (rotation == null) return false; + relativeOrientationDesiredRefreshRate = refresh_rate; + if (!paused) { + sensorManager.registerListener(relativeOrientationListener, rotation, getSensorDelay(refresh_rate)); + } + } + return true; + } + + public boolean stopOrientation() { + if (sensorManager == null) return false; + if (orientationAccelerometer == null && orientationMagnetometer == null && rotation == null) return true; + if (!paused) { + if (orientationAccelerometer != null) { + sensorManager.unregisterListener(absoluteOrientationListener, orientationAccelerometer); + } + if (orientationMagnetometer != null) { + sensorManager.unregisterListener(absoluteOrientationListener, orientationMagnetometer); + } + if (rotation != null) { + sensorManager.unregisterListener(relativeOrientationListener, rotation); + } + } + if (absoluteOrientationListenerPostponed != null) { + AndroidUtilities.cancelRunOnUIThread(absoluteOrientationListenerPostponed); + absoluteOrientationListenerPostponed = null; + } + if (relativeOrientationListenerPostponed != null) { + AndroidUtilities.cancelRunOnUIThread(relativeOrientationListenerPostponed); + relativeOrientationListenerPostponed = null; + } + orientationAccelerometer = null; + orientationMagnetometer = null; + rotation = null; + return true; + } + + public void stopAll() { + stopOrientation(); + stopGyroscope(); + stopAccelerometer(); + } + + // SENSOR_DELAY_NORMAL — 160ms + // SENSOR_DELAY_UI — 60ms + // SENSOR_DELAY_GAME — 20ms + private static int getSensorDelay(long refresh_rate) { + if (refresh_rate >= 160) return SensorManager.SENSOR_DELAY_NORMAL; + if (refresh_rate >= 60) return SensorManager.SENSOR_DELAY_UI; + return SensorManager.SENSOR_DELAY_GAME; + } + + private boolean paused; + + public void pause() { + if (paused) return; + paused = true; + + if (sensorManager != null) { + if (accelerometer != null) { + sensorManager.unregisterListener(accelerometerListener, accelerometer); + } + if (accelerometerListenerPostponed != null) { + AndroidUtilities.cancelRunOnUIThread(accelerometerListenerPostponed); + accelerometerListenerPostponed = null; + } + if (gyroscope != null) { + sensorManager.unregisterListener(gyroscopeListener, gyroscope); + } + if (gyroscopeListenerPostponed != null) { + AndroidUtilities.cancelRunOnUIThread(gyroscopeListenerPostponed); + gyroscopeListenerPostponed = null; + } + if (orientationAccelerometer != null) { + sensorManager.unregisterListener(absoluteOrientationListener, orientationAccelerometer); + } + if (orientationMagnetometer != null) { + sensorManager.unregisterListener(absoluteOrientationListener, orientationMagnetometer); + } + if (absoluteOrientationListenerPostponed != null) { + AndroidUtilities.cancelRunOnUIThread(absoluteOrientationListenerPostponed); + absoluteOrientationListenerPostponed = null; + } + if (rotation != null) { + sensorManager.unregisterListener(relativeOrientationListener, rotation); + } + if (relativeOrientationListenerPostponed != null) { + AndroidUtilities.cancelRunOnUIThread(relativeOrientationListenerPostponed); + relativeOrientationListenerPostponed = null; + } + } + } + + public void resume() { + if (!paused) return; + paused = false; + + if (sensorManager != null) { + if (accelerometer != null) { + sensorManager.registerListener(accelerometerListener, accelerometer, getSensorDelay(accelerometerDesiredRefreshRate)); + } + if (gyroscope != null) { + sensorManager.registerListener(gyroscopeListener, gyroscope, getSensorDelay(gyroscopeDesiredRefreshRate)); + } + if (orientationAccelerometer != null) { + sensorManager.registerListener(absoluteOrientationListener, orientationAccelerometer, getSensorDelay(absoluteOrientationDesiredRefreshRate)); + } + if (orientationMagnetometer != null) { + sensorManager.registerListener(absoluteOrientationListener, orientationMagnetometer, getSensorDelay(absoluteOrientationDesiredRefreshRate)); + } + if (rotation != null) { + sensorManager.registerListener(relativeOrientationListener, rotation, getSensorDelay(relativeOrientationDesiredRefreshRate)); + } + } + } + + private Runnable accelerometerListenerPostponed; + private final SensorEventListener accelerometerListener = new SensorEventListener() { + private float[] xyz; + private long lastTime; + @Override + public void onSensorChanged(SensorEvent event) { + if (accelerometerListenerPostponed != null) { + AndroidUtilities.cancelRunOnUIThread(accelerometerListenerPostponed); + accelerometerListenerPostponed = null; + } + if (paused || webView == null) return; + final long now = System.currentTimeMillis(); + final long diff = now - lastTime; + xyz = event.values; + if (diff < accelerometerDesiredRefreshRate) { + AndroidUtilities.runOnUIThread(accelerometerListenerPostponed = this::post, accelerometerDesiredRefreshRate - diff); + return; + } + post(); + } + + public void post() { + if (webView == null) return; + if (xyz == null) return; + lastTime = System.currentTimeMillis(); + try { + JSONObject eventData = new JSONObject(); + eventData.put("x", -xyz[0]); + eventData.put("y", -xyz[1]); + eventData.put("z", -xyz[2]); + webView.evaluateJS("window.Telegram.WebView.receiveEvent('" + "accelerometer_changed" + "', " + eventData + ");"); + } catch (Exception e) {} + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + + } + }; + + private Runnable gyroscopeListenerPostponed; + private final SensorEventListener gyroscopeListener = new SensorEventListener() { + + private long lastTime; + private float[] captured = new float[3]; + + + @Override + public void onSensorChanged(SensorEvent event) { + if (gyroscopeListenerPostponed != null) { + AndroidUtilities.cancelRunOnUIThread(gyroscopeListenerPostponed); + gyroscopeListenerPostponed = null; + } + if (paused || webView == null) return; + captured[0] += event.values[0]; + captured[1] += event.values[1]; + captured[2] += event.values[2]; + final long now = System.currentTimeMillis(); + final long diff = now - lastTime; + if (diff < gyroscopeDesiredRefreshRate) { + AndroidUtilities.runOnUIThread(gyroscopeListenerPostponed = this::post, gyroscopeDesiredRefreshRate - diff); + return; + } + post(); + } + + public void post() { + if (webView == null) return; + lastTime = System.currentTimeMillis(); + final float[] xyz = captured; + try { + JSONObject eventData = new JSONObject(); + eventData.put("x", xyz[0]); + eventData.put("y", xyz[1]); + eventData.put("z", xyz[2]); + // web api: +// eventData.put("x", xyz[2]); +// eventData.put("y", xyz[0]); +// eventData.put("z", xyz[1]); + webView.evaluateJS("window.Telegram.WebView.receiveEvent('" + "gyroscope_changed" + "', " + eventData + ");"); + } catch (Exception e) {} + captured[0] = 0; + captured[1] = 0; + captured[2] = 0; + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + + } + }; + + private Runnable absoluteOrientationListenerPostponed; + private final SensorEventListener absoluteOrientationListener = new SensorEventListener() { + private long lastTime; + + private float[] gravity; + private float[] geomagnetic; + + @Override + public void onSensorChanged(SensorEvent event) { + if (absoluteOrientationListenerPostponed != null) { + AndroidUtilities.cancelRunOnUIThread(absoluteOrientationListenerPostponed); + absoluteOrientationListenerPostponed = null; + } + if (paused || webView == null) return; + final long now = System.currentTimeMillis(); + final long diff = now - lastTime; + if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) + gravity = event.values; + if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) + geomagnetic = event.values; + if (diff < absoluteOrientationDesiredRefreshRate) { + AndroidUtilities.runOnUIThread(absoluteOrientationListenerPostponed = this::post, absoluteOrientationDesiredRefreshRate - diff); + return; + } + post(); + } + + public void post() { + if (gravity == null || geomagnetic == null) return; + if (webView == null) return; + lastTime = System.currentTimeMillis(); + float R[] = new float[9]; + float I[] = new float[9]; + if (SensorManager.getRotationMatrix(R, I, gravity, geomagnetic)) { + float orientation[] = new float[3]; + SensorManager.getOrientation(R, orientation); + try { + JSONObject eventData = new JSONObject(); + eventData.put("absolute", true); + eventData.put("alpha", -orientation[0]); + eventData.put("beta", -orientation[1]); + eventData.put("gamma", orientation[2]); + webView.evaluateJS("window.Telegram.WebView.receiveEvent('" + "device_orientation_changed" + "', " + eventData + ");"); + } catch (Exception e) {} + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + + } + }; + + private Runnable relativeOrientationListenerPostponed; + private final SensorEventListener relativeOrientationListener = new SensorEventListener() { + private long lastTime; + private float[] values; + @Override + public void onSensorChanged(SensorEvent event) { + if (relativeOrientationListenerPostponed != null) { + AndroidUtilities.cancelRunOnUIThread(relativeOrientationListenerPostponed); + relativeOrientationListenerPostponed = null; + } + if (paused || webView == null) return; + final long now = System.currentTimeMillis(); + final long diff = now - lastTime; + if (diff < relativeOrientationDesiredRefreshRate) { + AndroidUtilities.runOnUIThread(relativeOrientationListenerPostponed = this::post, relativeOrientationDesiredRefreshRate - diff); + return; + } + if (event.sensor.getType() == Sensor.TYPE_GAME_ROTATION_VECTOR) { + values = event.values; + } + post(); + } + + private float[] mDeviceRotationMatrix; + private float[] mTruncatedRotationVector; + + public void post() { + if (values == null) return; + if (webView == null) return; + lastTime = System.currentTimeMillis(); + if (mDeviceRotationMatrix == null) { + mDeviceRotationMatrix = new float[9]; + } + if (mTruncatedRotationVector == null) { + mTruncatedRotationVector = new float[4]; + } + if (values.length > 4) { + // On some Samsung devices SensorManager.getRotationMatrixFromVector + // appears to throw an exception if rotation vector has length > 4. + // For the purposes of this class the first 4 values of the + // rotation vector are sufficient (see crbug.com/335298 for details). + System.arraycopy(values, 0, mTruncatedRotationVector, 0, 4); + SensorManager.getRotationMatrixFromVector(mDeviceRotationMatrix, mTruncatedRotationVector); + } else { + SensorManager.getRotationMatrixFromVector(mDeviceRotationMatrix, values); + } + float orientation[] = new float[3]; + SensorManager.getOrientation(mDeviceRotationMatrix, orientation); + try { + JSONObject eventData = new JSONObject(); + eventData.put("absolute", false); + eventData.put("alpha", -orientation[0]); + eventData.put("beta", -orientation[1]); + eventData.put("gamma", orientation[2]); + webView.evaluateJS("window.Telegram.WebView.receiveEvent('" + "device_orientation_changed" + "', " + eventData + ");"); + } catch (Exception e) {} + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + + } + }; + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotShareSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotShareSheet.java new file mode 100644 index 00000000000..4f474bb6b01 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotShareSheet.java @@ -0,0 +1,621 @@ +package org.telegram.ui.bots; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.content.Context; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.provider.MediaStore; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.LinearLayout; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.ChatListItemAnimator; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.GridLayoutManagerFixed; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.google.android.exoplayer2.source.dash.DashChunkSource; + +import org.telegram.messenger.AccountInstance; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.DialogObject; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.ImageLoader; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MediaDataController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.MessagesStorage; +import org.telegram.messenger.R; +import org.telegram.messenger.SendMessagesHelper; +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.AlertDialog; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.ChatActionCell; +import org.telegram.ui.Cells.ChatMessageCell; +import org.telegram.ui.ChatActivity; +import org.telegram.ui.Components.BottomSheetWithRecyclerListView; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.SizeNotifierFrameLayout; +import org.telegram.ui.Components.UItem; +import org.telegram.ui.Components.UniversalAdapter; +import org.telegram.ui.DialogsActivity; +import org.telegram.ui.LaunchActivity; +import org.telegram.ui.Stories.recorder.ButtonWithCounterView; +import org.telegram.ui.Stories.recorder.PreviewView; +import org.telegram.ui.web.HttpGetFileTask; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; + +public class BotShareSheet extends BottomSheetWithRecyclerListView { + + private final int currentAccount; + private final long botId; + private final String botName; + private final TLRPC.TL_messages_preparedInlineMessage message; + + private UniversalAdapter adapter; + + private final ChatMessageCell messageCell; + private final ChatActionCell actionCell; + private MessageObject messageObject; + private final LinearLayout chatListView; + private final SizeNotifierFrameLayout chatView; + + private final FrameLayout buttonContainer; + private final ButtonWithCounterView button; + + public static void share(Context context, int currentAccount, long botId, String id, Theme.ResourcesProvider resourcesProvider, Runnable whenOpened, Utilities.Callback2> whenDone) { + TLRPC.TL_messages_getPreparedInlineMessage req = new TLRPC.TL_messages_getPreparedInlineMessage(); + req.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); + req.id = id; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (res instanceof TLRPC.TL_messages_preparedInlineMessage) { + TLRPC.TL_messages_preparedInlineMessage result = (TLRPC.TL_messages_preparedInlineMessage) res; + final File[] finalFile = new File[1]; + Runnable open = () -> { + new BotShareSheet(context, currentAccount, botId, id, result, finalFile[0], resourcesProvider, whenOpened, whenDone).show(); + }; + if (result != null && result.result.content != null && !TextUtils.isEmpty(result.result.content.url) && result.result.send_message instanceof TLRPC.TL_botInlineMessageMediaAuto) { + final String url = result.result.content.url; + String ext = ImageLoader.getHttpUrlExtension(url, null); + if (TextUtils.isEmpty(ext)) { + ext = FileLoader.getExtensionByMimeType(result.result.content.mime_type); + } else { + ext = "." + ext; + } + final File file = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), Utilities.MD5(url) + ext); + if (!file.exists()) { + final AlertDialog progressDialog = new AlertDialog(context, AlertDialog.ALERT_TYPE_SPINNER); + HttpGetFileTask fileLoader = new HttpGetFileTask(f -> { + finalFile[0] = f; + progressDialog.dismiss(); + open.run(); + }); + fileLoader.setDestFile(file); + fileLoader.setMaxSize(8 * 1024 * 1024); + fileLoader.execute(url); + progressDialog.setOnCancelListener(v -> { + fileLoader.cancel(true); + }); + progressDialog.showDelayed(180); + } else { + open.run(); + } + } else { + open.run(); + } + } else { + if (whenDone != null) { + whenDone.run("MESSAGE_EXPIRED", null); + } + } + })); + } + + private boolean openedDialogsActivity = false; + private boolean sent = false; + private final Utilities.Callback2> whenDone; + + public BotShareSheet(Context context, int currentAccount, long botId, String id, TLRPC.TL_messages_preparedInlineMessage message, File file, Theme.ResourcesProvider resourcesProvider, Runnable whenOpened, Utilities.Callback2> whenDone) { + super(context, null, false, false, false, resourcesProvider); + this.currentAccount = currentAccount; + this.message = message; + this.botId = botId; + this.botName = UserObject.getUserName(MessagesController.getInstance(currentAccount).getUser(botId)); + this.whenDone = whenDone; + + fixNavigationBar(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider)); + setSlidingActionBar(); + headerPaddingTop = dp(4); + headerPaddingBottom = dp(-10); + + messageObject = convert(currentAccount, botId, message.result, file); + + actionCell = new ChatActionCell(context, false, resourcesProvider); + actionCell.setDelegate(new ChatActionCell.ChatActionCellDelegate() {}); + actionCell.setCustomText(LocaleController.getString(R.string.BotShareMessagePreview)); + + messageCell = new ChatMessageCell(context, currentAccount) { + @Override + public boolean isDrawSelectionBackground() { + return false; + } + }; + messageCell.setDelegate(new ChatMessageCell.ChatMessageCellDelegate() { + @Override + public boolean canPerformActions() { + return false; + } + }); + messageCell.setMessageObject(messageObject, null, false, false); + + chatListView = new LinearLayout(context); + chatListView.setOrientation(LinearLayout.VERTICAL); + + chatListView.addView(actionCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + chatListView.addView(messageCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + chatView = new SizeNotifierFrameLayout(context) { + @Override + protected boolean isActionBarVisible() { + return false; + } + @Override + protected boolean isStatusBarVisible() { + return false; + } + @Override + protected boolean useRootView() { + return false; + } + }; + chatView.setBackgroundImage(PreviewView.getBackgroundDrawable(null, currentAccount, botId, Theme.isCurrentThemeDark()), false); + chatView.addView(chatListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL, 4, 8, 4, 8)); + + buttonContainer = new FrameLayout(context); + buttonContainer.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider)); + + button = new ButtonWithCounterView(context, resourcesProvider); + button.setText(LocaleController.getString(R.string.BotShareMessageShare), false); + button.setOnClickListener(v -> { + BaseFragment lastFragment = LaunchActivity.getSafeLastFragment(); + if (lastFragment == null) return; + + openedDialogsActivity = true; + final Bundle args = new Bundle(); + args.putBoolean("onlySelect", true); + args.putBoolean("canSelectTopics", true); + args.putInt("dialogsType", DialogsActivity.DIALOGS_TYPE_BOT_SHARE); + + if (!message.peer_types.isEmpty()) { + args.putBoolean("allowGroups", false); + args.putBoolean("allowMegagroups", false); + args.putBoolean("allowLegacyGroups", false); + args.putBoolean("allowUsers", false); + args.putBoolean("allowChannels", false); + args.putBoolean("allowBots", false); + for (TLRPC.InlineQueryPeerType peerType : message.peer_types) { + if (peerType instanceof TLRPC.TL_inlineQueryPeerTypePM) { + args.putBoolean("allowUsers", true); + } else if (peerType instanceof TLRPC.TL_inlineQueryPeerTypeBotPM) { + args.putBoolean("allowBots", true); + } else if (peerType instanceof TLRPC.TL_inlineQueryPeerTypeBroadcast) { + args.putBoolean("allowChannels", true); + } else if (peerType instanceof TLRPC.TL_inlineQueryPeerTypeChat) { + args.putBoolean("allowLegacyGroups", true); + } else if (peerType instanceof TLRPC.TL_inlineQueryPeerTypeMegagroup) { + args.putBoolean("allowMegagroups", true); + } + } + } + + final DialogsActivity fragment = new DialogsActivity(args) { + @Override + public boolean clickSelectsDialog() { + return true; + } + @Override + public void onFragmentDestroy() { + super.onFragmentDestroy(); + if (!sent) { + sent = true; + if (whenDone != null) { + whenDone.run("USER_DECLINED", null); + } + } + } + }; + fragment.setDelegate((fragment1, dids, _message, param, notify, scheduleDate, topicsFragment) -> { + ArrayList dialogIds = new ArrayList<>(); + for (MessagesStorage.TopicKey key : dids) { + final long dialogId = key.dialogId; + final long topicId = key.topicId; + + if (DialogObject.isEncryptedDialog(dialogId)) { + continue; + } + + MessageObject replyToMsg = null; + if (topicId != 0) { + TLRPC.TL_forumTopic topic = MessagesController.getInstance(currentAccount).getTopicsController().findTopic(-dialogId, topicId); + if (topic != null && topic.topicStartMessage != null) { + replyToMsg = new MessageObject(currentAccount, topic.topicStartMessage, false, false); + replyToMsg.isTopicMainMessage = true; + } + } + + HashMap params = new HashMap<>(); + params.put("query_id", "" + message.query_id); + params.put("id", "" + message.result.id); + params.put("bot", "" + botId); + SendMessagesHelper.prepareSendingBotContextResult(lastFragment, AccountInstance.getInstance(currentAccount), message.result, params, dialogId, replyToMsg, replyToMsg, null, null, notify, scheduleDate, null, 0); + if (_message != null) { + SendMessagesHelper.getInstance(currentAccount).sendMessage(SendMessagesHelper.SendMessageParams.of(_message.toString(), dialogId, replyToMsg, replyToMsg, null, true, null, null, null, true, 0, null, false)); + } + dialogIds.add(dialogId); + } + if (!sent) { + sent = true; + if (whenDone != null) { + whenDone.run(dialogIds.size() > 0 ? null : "USER_DECLINED", dialogIds); + } + } + if (topicsFragment != null) { + topicsFragment.finishFragment(); + fragment1.removeSelfFromStack(); + } else { + fragment1.finishFragment(); + } + return true; + }); + lastFragment.presentFragment(fragment); + dismiss(); + if (whenOpened != null) { + whenOpened.run(); + } + }); + buttonContainer.addView(button, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.FILL, 10, 10, 10, 10)); + + containerView.addView(buttonContainer, LayoutHelper.createFrameMarginPx(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, backgroundPaddingLeft, 0, backgroundPaddingLeft, 0)); + recyclerListView.setPadding(0, 0, 0, dp(10 + 48 + 10) + 1); + + adapter.update(false); + +// if (message.result.content != null && !TextUtils.isEmpty(message.result.content.url) && message.result.send_message instanceof TLRPC.TL_botInlineMessageMediaAuto) { +// final String url = message.result.content.url; +// String ext = ImageLoader.getHttpUrlExtension(url, null); +// if (TextUtils.isEmpty(ext)) { +// ext = FileLoader.getExtensionByMimeType(message.result.content.mime_type); +// } else { +// ext = "." + ext; +// } +// final File file = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), Utilities.MD5(url) + ext); +// if (file.exists()) { +// applyFile(file); +// } else { +// autoMediaLoader = new HttpGetFileTask(this::applyFile); +// autoMediaLoader.setDestFile(file); +// autoMediaLoader.setMaxSize(8 * 1024 * 1024); +// autoMediaLoader.execute(url); +// } +// } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final int itemsCount = recyclerListView.getAdapter() == null ? 0 : recyclerListView.getAdapter().getItemCount(); + recyclerListView.scrollToPosition(Math.max(itemsCount - 1, 0)); + } + + @Override + public void dismiss() { + super.dismiss(); + if (!openedDialogsActivity && !sent) { + sent = true; + if (whenDone != null) { + whenDone.run("USER_DECLINED", null); + } + } + } + + @Override + protected CharSequence getTitle() { + return LocaleController.getString(R.string.BotShareMessage); + } + + @Override + protected RecyclerListView.SelectionAdapter createAdapter(RecyclerListView listView) { + return adapter = new UniversalAdapter(listView, getContext(), currentAccount, 0, true, this::fillItems, resourcesProvider); + } + + public void fillItems(ArrayList items, UniversalAdapter adapter) { + items.add(UItem.asCustom(-1, chatView)); + items.add(UItem.asShadow(AndroidUtilities.replaceTags(LocaleController.formatString(R.string.BotShareMessageInfo, botName)))); + } + + public static MessageObject convert(int currentAccount, long botId, TLRPC.BotInlineResult result) { + return convert(currentAccount, botId, result, null, null); + } + + public static MessageObject convert(int currentAccount, long botId, TLRPC.BotInlineResult result, File file) { + if (file == null || !file.exists()) + return convert(currentAccount, botId, result, null, null); + + final String type = result.type; + final String finalPath = file.getAbsolutePath(); + + TLRPC.Document document = null; + TLRPC.TL_photo photo = null; + switch (type) { + case "audio": + case "voice": + case "file": + case "video": + case "sticker": + case "gif": { + document = new TLRPC.TL_document(); + document.id = 0; + document.size = 0; + document.dc_id = 0; + document.mime_type = result.content.mime_type; + document.file_reference = new byte[0]; + document.date = ConnectionsManager.getInstance(currentAccount).getCurrentTime(); + TLRPC.TL_documentAttributeFilename fileName = new TLRPC.TL_documentAttributeFilename(); + document.attributes.add(fileName); + + switch (type) { + case "gif": { + fileName.file_name = "animation.gif"; + if (finalPath.endsWith("mp4")) { + document.mime_type = "video/mp4"; + document.attributes.add(new TLRPC.TL_documentAttributeAnimated()); + } else { + document.mime_type = "image/gif"; + } + break; + } + case "voice": { + TLRPC.TL_documentAttributeAudio audio = new TLRPC.TL_documentAttributeAudio(); + audio.duration = MessageObject.getInlineResultDuration(result); + audio.voice = true; + fileName.file_name = "audio.ogg"; + document.attributes.add(audio); + break; + } + case "audio": { + TLRPC.TL_documentAttributeAudio audio = new TLRPC.TL_documentAttributeAudio(); + audio.duration = MessageObject.getInlineResultDuration(result); + audio.title = result.title; + audio.flags |= 1; + if (result.description != null) { + audio.performer = result.description; + audio.flags |= 2; + } + fileName.file_name = "audio.mp3"; + document.attributes.add(audio); + break; + } + case "file": { + int idx = result.content.mime_type.lastIndexOf('/'); + if (idx != -1) { + fileName.file_name = "file." + result.content.mime_type.substring(idx + 1); + } else { + fileName.file_name = "file"; + } + break; + } + case "video": { + fileName.file_name = "video.mp4"; + TLRPC.TL_documentAttributeVideo attributeVideo = new TLRPC.TL_documentAttributeVideo(); + int wh[] = MessageObject.getInlineResultWidthAndHeight(result); + attributeVideo.w = wh[0]; + attributeVideo.h = wh[1]; + attributeVideo.duration = MessageObject.getInlineResultDuration(result); + attributeVideo.supports_streaming = true; + document.attributes.add(attributeVideo); + try { + if (result.thumb != null) { + String thumbPath = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), Utilities.MD5(result.thumb.url) + "." + ImageLoader.getHttpUrlExtension(result.thumb.url, "jpg")).getAbsolutePath(); + Bitmap bitmap = ImageLoader.loadBitmap(thumbPath, null, 90, 90, true); + if (bitmap != null) { + TLRPC.PhotoSize thumb = ImageLoader.scaleAndSaveImage(bitmap, 90, 90, 55, false); + if (thumb != null) { + document.thumbs.add(thumb); + document.flags |= 1; + } + bitmap.recycle(); + } + } + } catch (Throwable e) { + FileLog.e(e); + } + break; + } + case "sticker": { + TLRPC.TL_documentAttributeSticker attributeSticker = new TLRPC.TL_documentAttributeSticker(); + attributeSticker.alt = ""; + attributeSticker.stickerset = new TLRPC.TL_inputStickerSetEmpty(); + document.attributes.add(attributeSticker); + TLRPC.TL_documentAttributeImageSize attributeImageSize = new TLRPC.TL_documentAttributeImageSize(); + int wh[] = MessageObject.getInlineResultWidthAndHeight(result); + attributeImageSize.w = wh[0]; + attributeImageSize.h = wh[1]; + document.attributes.add(attributeImageSize); + fileName.file_name = "sticker.webp"; + try { + if (result.thumb != null) { + String thumbPath = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), Utilities.MD5(result.thumb.url) + "." + ImageLoader.getHttpUrlExtension(result.thumb.url, "webp")).getAbsolutePath(); + Bitmap bitmap = ImageLoader.loadBitmap(thumbPath, null, 90, 90, true); + if (bitmap != null) { + TLRPC.PhotoSize thumb = ImageLoader.scaleAndSaveImage(bitmap, 90, 90, 55, false); + if (thumb != null) { + document.thumbs.add(thumb); + document.flags |= 1; + } + bitmap.recycle(); + } + } + } catch (Throwable e) { + FileLog.e(e); + } + break; + } + } + if (fileName.file_name == null) { + fileName.file_name = "file"; + } + if (document.mime_type == null) { + document.mime_type = "application/octet-stream"; + } + if (document.thumbs.isEmpty()) { + TLRPC.PhotoSize thumb = new TLRPC.TL_photoSize(); + int wh[] = MessageObject.getInlineResultWidthAndHeight(result); + thumb.w = wh[0]; + thumb.h = wh[1]; + thumb.size = 0; + thumb.location = new TLRPC.TL_fileLocationUnavailable(); + thumb.type = "x"; + + document.thumbs.add(thumb); + document.flags |= 1; + } + break; + } + case "photo": { + if (file.exists()) { + photo = SendMessagesHelper.getInstance(currentAccount).generatePhotoSizes(finalPath, null); + } + if (photo == null) { + photo = new TLRPC.TL_photo(); + photo.date = ConnectionsManager.getInstance(currentAccount).getCurrentTime(); + photo.file_reference = new byte[0]; + TLRPC.TL_photoSize photoSize = new TLRPC.TL_photoSize(); + int wh[] = MessageObject.getInlineResultWidthAndHeight(result); + photoSize.w = wh[0]; + photoSize.h = wh[1]; + photoSize.size = 1; + photoSize.location = new TLRPC.TL_fileLocationUnavailable(); + photoSize.type = "x"; + photo.sizes.add(photoSize); + } + break; + } + } + return convert(currentAccount, botId, result, photo, document); + } + + public static MessageObject convert(int currentAccount, long botId, TLRPC.BotInlineResult result, TLRPC.Photo photo, TLRPC.Document document) { + if (photo == null) photo = result.photo; + if (document == null) document = result.document; + + final TLRPC.TL_message msg = new TLRPC.TL_message(); + + msg.out = false; + msg.flags |= 2048; + msg.via_bot_id = botId; + msg.date = ConnectionsManager.getInstance(currentAccount).getCurrentTime(); + + msg.peer_id = MessagesController.getInstance(currentAccount).getPeer(UserConfig.getInstance(currentAccount).getClientUserId()); + msg.from_id = MessagesController.getInstance(currentAccount).getPeer(UserConfig.getInstance(currentAccount).getClientUserId()); + + if (result.send_message != null) { + final TLRPC.BotInlineMessage message = result.send_message; + if (message instanceof TLRPC.TL_botInlineMessageText) { + TLRPC.TL_botInlineMessageText m = (TLRPC.TL_botInlineMessageText) message; + msg.message = m.message; + msg.entities = m.entities; + } else if (message instanceof TLRPC.TL_botInlineMessageMediaContact) { + TLRPC.TL_botInlineMessageMediaContact m = (TLRPC.TL_botInlineMessageMediaContact) message; + TLRPC.TL_messageMediaContact media = new TLRPC.TL_messageMediaContact(); + media.phone_number = m.phone_number; + media.first_name = m.first_name; + media.last_name = m.last_name; + media.vcard = m.vcard; + msg.flags |= 512; + msg.media = media; + } else if (message instanceof TLRPC.TL_botInlineMessageMediaGeo) { + TLRPC.TL_botInlineMessageMediaGeo m = (TLRPC.TL_botInlineMessageMediaGeo) message; + TLRPC.TL_messageMediaGeo media = new TLRPC.TL_messageMediaGeo(); + media.geo = m.geo; + msg.flags |= 512; + msg.media = media; + } else if (message instanceof TLRPC.TL_botInlineMessageMediaVenue) { + TLRPC.TL_botInlineMessageMediaVenue m = (TLRPC.TL_botInlineMessageMediaVenue) message; + TLRPC.TL_messageMediaVenue media = new TLRPC.TL_messageMediaVenue(); + media.geo = m.geo; + media.title = m.title; + media.address = m.address; + media.provider = m.provider; + media.venue_id = m.venue_id; + media.provider = m.venue_type; + msg.flags |= 512; + msg.media = media; + } else if (message instanceof TLRPC.TL_botInlineMessageMediaAuto) { + TLRPC.TL_botInlineMessageMediaAuto m = (TLRPC.TL_botInlineMessageMediaAuto) message; + msg.message = m.message; + msg.entities = m.entities; + } else if (message instanceof TLRPC.TL_botInlineMessageMediaInvoice) { + TLRPC.TL_botInlineMessageMediaInvoice m = (TLRPC.TL_botInlineMessageMediaInvoice) message; + } else if (message instanceof TLRPC.TL_botInlineMessageMediaWebPage) { + TLRPC.TL_botInlineMessageMediaWebPage m = (TLRPC.TL_botInlineMessageMediaWebPage) message; + TLRPC.TL_messageMediaWebPage media = new TLRPC.TL_messageMediaWebPage(); + media.force_large_media = m.force_large_media; + media.force_small_media = m.force_small_media; + media.manual = m.manual; + media.safe = m.safe; + media.webpage = new TLRPC.TL_webPageEmpty(); + msg.flags |= 512; + msg.media = media; + } + } + + if (photo != null) { + TLRPC.TL_messageMediaPhoto media = new TLRPC.TL_messageMediaPhoto(); + media.photo = photo; + msg.flags |= 512; + msg.media = media; + } else if (document != null) { + TLRPC.TL_messageMediaDocument media = new TLRPC.TL_messageMediaDocument(); + media.flags |= 1; + media.voice = "voice".equalsIgnoreCase(result.type); + media.round = "round".equalsIgnoreCase(result.type); + media.document = document; + msg.flags |= 512; + msg.media = media; + } + + if (result.send_message != null && result.send_message.reply_markup != null) { + msg.flags |= 64; + msg.reply_markup = result.send_message.reply_markup; + } + + final MessageObject messageObject = new MessageObject(currentAccount, msg, true, true) { + @Override + public boolean isOut() { + return false; + } + @Override + public boolean isOutOwner() { + return false; + } + }; + return messageObject; + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewAttachedSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewAttachedSheet.java index f30133d332e..272dada9a9f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewAttachedSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewAttachedSheet.java @@ -21,6 +21,7 @@ import android.graphics.PorterDuffColorFilter; import android.graphics.RectF; import android.graphics.drawable.Drawable; +import android.icu.util.Measure; import android.os.Build; import android.os.Bundle; import android.text.TextPaint; @@ -29,6 +30,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.widget.FrameLayout; import android.widget.TextView; import androidx.annotation.IntDef; @@ -56,6 +58,7 @@ import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.SendMessagesHelper; +import org.telegram.messenger.UserConfig; import org.telegram.messenger.UserObject; import org.telegram.messenger.Utilities; import org.telegram.messenger.browser.Browser; @@ -173,6 +176,7 @@ public WindowView getWindowView() { private long lastSwipeTime; private ChatAttachAlertBotWebViewLayout.WebViewSwipeContainer swipeContainer; + private FrameLayout.LayoutParams swipeContainerLayoutParams; private BotWebViewContainer webViewContainer; private ChatAttachAlertBotWebViewLayout.WebProgressView progressView; private Theme.ResourcesProvider resourcesProvider; @@ -274,10 +278,11 @@ public boolean restoreState(BaseFragment fragment, BottomSheetTabs.WebTabData ta botButtons.setState(tab.buttons, false); } needCloseConfirmation = tab.confirmDismiss; + currentAccount = tab.props != null ? tab.props.currentAccount : UserConfig.selectedAccount; if (tab.webView != null) { // tab.webView.resumeTimers(); tab.webView.onResume(); - webViewContainer.replaceWebView(tab.webView, tab.proxy); + webViewContainer.replaceWebView(currentAccount, tab.webView, tab.proxy); webViewContainer.setState(tab.ready || tab.webView.isPageLoaded(), tab.lastUrl); if (Theme.isCurrentThemeDark() != tab.themeIsDark) { // webViewContainer.notifyThemeChanged(); @@ -416,8 +421,8 @@ public void requestLayout() { swipeContainer.setShouldWaitWebViewScroll(true); webViewContainer = new BotWebViewContainer(getContext(), resourcesProvider, getColor(Theme.key_windowBackgroundWhite), true) { @Override - public void onWebViewCreated() { - super.onWebViewCreated(); + public void onWebViewCreated(MyWebView webView) { + super.onWebViewCreated(webView); swipeContainer.setWebView(webViewContainer.getWebView()); } }; @@ -740,14 +745,8 @@ public void onAnimationEnd(Animator animation) { lastSwipeTime = System.currentTimeMillis(); }); swipeContainer.setScrollEndListener(() -> webViewContainer.invalidateViewPortHeight(true)); - swipeContainer.setDelegate(() -> { -// if (can_minimize) { - dismiss(true, null); -// } else { -// if (!onCheckDismissByUser()) { -// swipeContainer.stickTo(0); -// } -// } + swipeContainer.setDelegate(byTap -> { + dismiss(true, null); }); swipeContainer.setTopActionBarOffsetY(ActionBar.getCurrentActionBarHeight() + AndroidUtilities.statusBarHeight - dp(24)); swipeContainer.setIsKeyboardVisible(obj -> windowView.getKeyboardHeight() >= dp(20)); @@ -922,6 +921,11 @@ public boolean setDialog(BottomSheetTabDialog dialog) { return true; } + @Override + public boolean hadDialog() { + return dialog != null; + } + Drawable verifiedDrawable; public void requestWebView(BaseFragment fragment, WebViewRequestProps props) { @@ -997,7 +1001,7 @@ public int getIntrinsicWidth() { settingsItem = otherItem.addSubItem(R.id.menu_settings, R.drawable.msg_settings, LocaleController.getString(R.string.BotWebViewSettings)); settingsItem.setVisibility(View.GONE); otherItem.addSubItem(R.id.menu_reload_page, R.drawable.msg_retry, LocaleController.getString(R.string.BotWebViewReloadPage)); - if (currentBot != null && MediaDataController.getInstance(currentAccount).canCreateAttachedMenuBotShortcut(currentBot.bot_id)) { + if (userbot != null && userbot.bot_has_main_app) { otherItem.addSubItem(R.id.menu_add_to_home_screen_bot, R.drawable.msg_home, LocaleController.getString(R.string.AddShortcut)); } otherItem.addSubItem(R.id.menu_share_bot, R.drawable.msg_share, LocaleController.getString(R.string.BotShare)); @@ -1082,6 +1086,7 @@ public void onItemClick(int id) { req.peer = MessagesController.getInstance(currentAccount).getInputPeer(botId); req.platform = "android"; req.compact = props.compact; + req.fullscreen = props.fullscreen; req.url = props.buttonUrl; req.flags |= 2; @@ -1111,6 +1116,7 @@ public void onItemClick(int id) { req.platform = "android"; req.from_side_menu = (props.flags & FLAG_FROM_SIDE_MENU) != 0; req.compact = props.compact; + req.fullscreen = props.fullscreen; if (themeParams != null) { req.theme_params = new TLRPC.TL_dataJSON(); req.theme_params.data = themeParams.toString(); @@ -1141,6 +1147,7 @@ public void onItemClick(int id) { req.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); req.platform = "android"; req.compact = props.compact; + req.fullscreen = props.fullscreen; if (props.buttonUrl != null) { req.url = props.buttonUrl; req.flags |= 2; @@ -1180,6 +1187,7 @@ public void onItemClick(int id) { req.peer = fragment instanceof ChatActivity ? ((ChatActivity) fragment).getCurrentUser() != null ? MessagesController.getInputPeer(((ChatActivity) fragment).getCurrentUser()) : MessagesController.getInputPeer(((ChatActivity) fragment).getCurrentChat()) : MessagesController.getInputPeer(props.botUser); req.compact = props.compact; + req.fullscreen = props.fullscreen; if (!TextUtils.isEmpty(props.startParam)) { req.start_param = props.startParam; @@ -1210,6 +1218,7 @@ public void onItemClick(int id) { req.peer = fragment instanceof ChatActivity ? ((ChatActivity) fragment).getCurrentUser() != null ? MessagesController.getInputPeer(((ChatActivity) fragment).getCurrentUser()) : MessagesController.getInputPeer(((ChatActivity) fragment).getCurrentChat()) : MessagesController.getInputPeer(props.botUser); req.compact = props.compact; + req.fullscreen = props.fullscreen; if (!TextUtils.isEmpty(props.startParam)) { req.start_param = props.startParam; @@ -1851,7 +1860,9 @@ public static void hasPrivacy(int currentAccount, long botId, Utilities.Callback return; } MessagesController.getInstance(currentAccount).loadFullUser(user, 0, true, (userFull2) -> { - whenDone.run(hasPrivacy(userFull2)); + AndroidUtilities.runOnUIThread(() -> { + whenDone.run(hasPrivacy(userFull2)); + }); }); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewMenuContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewMenuContainer.java index 9722c03a2e4..e8a0336d6ba 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewMenuContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewMenuContainer.java @@ -171,6 +171,11 @@ public boolean setDialog(BottomSheetTabDialog dialog) { return false; } + @Override + public boolean hadDialog() { + return false; + } + public BottomSheetTabs.WebTabData saveState() { preserving = true; BottomSheetTabs.WebTabData tab = new BottomSheetTabs.WebTabData(); @@ -221,7 +226,7 @@ public boolean restoreState(BaseFragment fragment, BottomSheetTabs.WebTabData ta if (tab.webView != null) { // tab.webView.resumeTimers(); tab.webView.onResume(); - webViewContainer.replaceWebView(tab.webView, tab.proxy); + webViewContainer.replaceWebView(currentAccount, tab.webView, tab.proxy); } else { tab.props.response = null; tab.props.responseTime = 0; @@ -278,8 +283,9 @@ public BotWebViewMenuContainer(@NonNull Context context, ChatActivityEnterView p webViewContainer = new BotWebViewContainer(context, parentEnterView.getParentFragment().getResourceProvider(), getColor(Theme.key_windowBackgroundWhite), true) { @Override - public void onWebViewCreated() { - swipeContainer.setWebView(webViewContainer.getWebView()); + public void onWebViewCreated(MyWebView webView) { + super.onWebViewCreated(webView); + swipeContainer.setWebView(webView); } }; webViewContainer.setDelegate(webViewDelegate = new BotWebViewContainer.Delegate() { @@ -528,10 +534,7 @@ public void requestLayout() { }); swipeContainer.setScrollEndListener(()-> webViewContainer.invalidateViewPortHeight(true)); swipeContainer.addView(webViewContainer); - swipeContainer.setDelegate(() -> { -// if (!onCheckDismissByUser()) { -// swipeContainer.stickTo(0); -// } + swipeContainer.setDelegate(byTap -> { dismiss(true, null); }); swipeContainer.setTopActionBarOffsetY(ActionBar.getCurrentActionBarHeight() + AndroidUtilities.statusBarHeight - AndroidUtilities.dp(24)); @@ -1081,7 +1084,8 @@ public void onDismiss() { webViewContainer = new BotWebViewContainer(getContext(), parentEnterView.getParentFragment().getResourceProvider(), getColor(Theme.key_windowBackgroundWhite), true) { @Override - public void onWebViewCreated() { + public void onWebViewCreated(MyWebView webView) { + super.onWebViewCreated(webView); swipeContainer.setWebView(webViewContainer.getWebView()); } }; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewSheet.java index 65ab88bf829..981beb128d9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewSheet.java @@ -1,6 +1,8 @@ package org.telegram.ui.bots; +import static org.telegram.messenger.AndroidUtilities.distanceInfluenceForSnapDuration; import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.lerp; import static org.telegram.ui.Components.Bulletin.DURATION_PROLONG; import android.animation.Animator; @@ -14,17 +16,22 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; +import android.graphics.Insets; 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.drawable.Drawable; import android.os.Build; import android.os.Bundle; +import android.text.SpannableStringBuilder; import android.text.TextPaint; import android.text.TextUtils; +import android.util.Log; +import android.util.Pair; import android.util.TypedValue; import android.view.Gravity; import android.view.MotionEvent; @@ -33,6 +40,7 @@ import android.view.Window; import android.view.WindowInsets; import android.view.WindowManager; +import android.widget.FrameLayout; import android.widget.TextView; import androidx.annotation.IntDef; @@ -41,12 +49,18 @@ import androidx.core.content.ContextCompat; import androidx.core.graphics.ColorUtils; import androidx.core.math.MathUtils; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; +import com.google.android.exoplayer2.offline.Download; +import com.google.android.gms.vision.Frame; + import org.json.JSONObject; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.AnimationNotificationsLocker; +import org.telegram.messenger.BotFullscreenButtons; import org.telegram.messenger.ContactsController; import org.telegram.messenger.DialogObject; import org.telegram.messenger.Emoji; @@ -59,12 +73,14 @@ import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.SendMessagesHelper; +import org.telegram.messenger.UserConfig; import org.telegram.messenger.UserObject; import org.telegram.messenger.Utilities; import org.telegram.messenger.browser.Browser; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; +import org.telegram.tgnet.tl.TL_bots; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; @@ -76,28 +92,36 @@ import org.telegram.ui.ActionBar.BottomSheetTabsOverlay; import org.telegram.ui.ActionBar.INavigationLayout; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ArticleViewer; import org.telegram.ui.ChatActivity; +import org.telegram.ui.Components.AnchorSpan; import org.telegram.ui.Components.Bulletin; import org.telegram.ui.Components.BulletinFactory; import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.ItemOptions; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.OverlayActionBarLayoutDialog; import org.telegram.ui.Components.PasscodeView; -import org.telegram.ui.Components.RadialProgressView; import org.telegram.ui.Components.SimpleFloatPropertyCompat; import org.telegram.ui.Components.SizeNotifierFrameLayout; import org.telegram.ui.Components.VerticalPositionAutoAnimator; import org.telegram.ui.DialogsActivity; import org.telegram.ui.LaunchActivity; import org.telegram.ui.PaymentFormActivity; +import org.telegram.ui.ProfileActivity; import org.telegram.ui.Stars.StarsController; import org.telegram.ui.web.BotWebViewContainer; import java.io.File; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Map; public class BotWebViewSheet extends Dialog implements NotificationCenter.NotificationCenterDelegate, BottomSheetTabsOverlay.Sheet { 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, TYPE_WEB_VIEW_BOT_MAIN = 4; @@ -106,6 +130,8 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi public final static int FLAG_FROM_SIDE_MENU = 2; private int lineColor; + public static HashSet activeSheets = new HashSet<>(); + public void showJustAddedBulletin() { TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(botId); TLRPC.TL_attachMenuBot currentBot = null; @@ -120,17 +146,18 @@ public void showJustAddedBulletin() { } String str; if (currentBot.show_in_side_menu && currentBot.show_in_attach_menu) { - str = LocaleController.formatString("BotAttachMenuShortcatAddedAttachAndSide", R.string.BotAttachMenuShortcatAddedAttachAndSide, user.first_name); + str = LocaleController.formatString(R.string.BotAttachMenuShortcatAddedAttachAndSide, user.first_name); } else if (currentBot.show_in_side_menu) { - str = LocaleController.formatString("BotAttachMenuShortcatAddedSide", R.string.BotAttachMenuShortcatAddedSide, user.first_name); + str = LocaleController.formatString(R.string.BotAttachMenuShortcatAddedSide, user.first_name); } else { - str = LocaleController.formatString("BotAttachMenuShortcatAddedAttach", R.string.BotAttachMenuShortcatAddedAttach, user.first_name); + str = LocaleController.formatString(R.string.BotAttachMenuShortcatAddedAttach, user.first_name); } AndroidUtilities.runOnUIThread(() -> { - BulletinFactory.of(windowView, resourcesProvider) + showBulletin(b -> + b .createSimpleBulletin(R.raw.contact_check, AndroidUtilities.replaceTags(str)) .setDuration(DURATION_PROLONG) - .show(true); + ); }, 200); } @@ -153,6 +180,7 @@ public void showJustAddedBulletin() { obj.actionBar.setAlpha(value); obj.updateLightStatusBar(); + obj.updateDownloadBulletinArrow(); }).setMultiplier(100f); private float actionBarTransitionProgress = 0f; private SpringAnimation springAnimation; @@ -160,10 +188,17 @@ public void showJustAddedBulletin() { private Boolean wasLightStatusBar; private WindowView windowView; + private final Rect navInsets = new Rect(); + private final Rect insets = new Rect(); + private int keyboardInset = 0; + + private BottomSheetTabs bottomTabs; + private BottomSheetTabs.ClipTools bottomTabsClip; private long lastSwipeTime; private ChatAttachAlertBotWebViewLayout.WebViewSwipeContainer swipeContainer; + private FrameLayout.LayoutParams swipeContainerLayoutParams; private BotWebViewContainer webViewContainer; private ChatAttachAlertBotWebViewLayout.WebProgressView progressView; private Theme.ResourcesProvider resourcesProvider; @@ -187,25 +222,36 @@ public void showJustAddedBulletin() { private boolean actionBarIsLight; private Paint actionBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private boolean overrideActionBarColor; private boolean overrideBackgroundColor; private ActionBar actionBar; + private FrameLayout.LayoutParams actionBarLayoutParams; private Drawable actionBarShadow; - private ActionBarMenuSubItem settingsItem; + private ActionBarMenuItem optionsItem; + private BotFullscreenButtons.OptionsIcon optionsIcon; + private boolean hasSettings; private TLRPC.BotApp currentWebApp; private boolean dismissed; + private boolean fullscreen; + private float fullscreenProgress; + private float fullscreenTransitionProgress; + private boolean fullscreenInProgress; + private int swipeContainerFromWidth, swipeContainerFromHeight; private Activity parentActivity; + private BotButtons botButtons; + private FrameLayout.LayoutParams botButtonsLayoutParams; + private BotFullscreenButtons fullscreenButtons; - private boolean mainButtonWasVisible, mainButtonProgressWasVisible; - private TextView mainButton; - private RadialProgressView radialProgressView; + private Bulletin downloadBulletin; + private BotDownloads.DownloadBulletin downloadBulletinLayout; + private FrameLayout bulletinContainer; + private FrameLayout.LayoutParams bulletinContainerLayoutParams; private boolean needCloseConfirmation; - private VerticalPositionAutoAnimator mainButtonAutoAnimator, radialProgressAutoAnimator; - private PasscodeView passcodeView; private Runnable pollRunnable = () -> { @@ -241,25 +287,37 @@ public void showJustAddedBulletin() { private Boolean fullsize = null; private boolean needsContext; + private BotSensors sensors; + private boolean orientationLocked; + + private BottomSheetTabs.WebTabData lastTab; + public BottomSheetTabs.WebTabData saveState() { BottomSheetTabs.WebTabData tab = new BottomSheetTabs.WebTabData(); tab.actionBarColor = actionBarColor; tab.actionBarColorKey = actionBarColorKey; - tab.overrideActionBarColor = overrideBackgroundColor; + tab.overrideActionBarColor = overrideActionBarColor; + tab.overrideBackgroundColor = overrideBackgroundColor; tab.backgroundColor = backgroundPaint.getColor(); tab.props = requestProps; tab.ready = webViewContainer != null && webViewContainer.isPageLoaded(); tab.themeIsDark = Theme.isCurrentThemeDark(); tab.lastUrl = webViewContainer != null ? webViewContainer.getUrlLoaded() : null; - tab.expanded = swipeContainer != null && swipeContainer.getSwipeOffsetY() < 0 || forceExpnaded || isFullSize(); - tab.fullsize = isFullSize(); + tab.expanded = swipeContainer != null && swipeContainer.getSwipeOffsetY() < 0 || forceExpnaded || isFullSize() || fullscreen; + tab.fullscreen = fullscreen; + tab.fullsize = (fullsize == null ? defaultFullsize : fullsize); tab.expandedOffset = swipeContainer != null ? swipeContainer.getOffsetY() : Float.MAX_VALUE; tab.needsContext = needsContext; tab.backButton = backButtonShown; - tab.main = mainButtonSettings; tab.confirmDismiss = needCloseConfirmation; - tab.settings = settingsItem != null && settingsItem.getVisibility() == View.VISIBLE; + tab.settings = hasSettings; tab.allowSwipes = swipeContainer == null || swipeContainer.isAllowedSwipes(); + tab.buttons = botButtons.state; + tab.navigationBarColor = navBarColor; + if (sensors != null) { + sensors.pause(); + } + tab.sensors = sensors; BotWebViewContainer.MyWebView webView = webViewContainer == null ? null : webViewContainer.getWebView(); if (webView != null) { webViewContainer.preserveWebView(); @@ -270,33 +328,57 @@ public BottomSheetTabs.WebTabData saveState() { webView.onPause(); // webView.pauseTimers(); } - return tab; + if (tab.error = errorShown) { + tab.errorDescription = errorCode; + } + tab.orientationLocked = orientationLocked; + return lastTab = tab; + } + + public Activity getActivity() { + Activity a = getOwnerActivity(); + if (a == null) a = LaunchActivity.instance; + if (a == null) a = AndroidUtilities.findActivity(getContext()); + return a; } + public boolean fromTab; public boolean showExpanded; public float showOffsetY; public boolean restoreState(BaseFragment fragment, BottomSheetTabs.WebTabData tab) { if (tab == null || tab.props == null) return false; - if (tab.overrideActionBarColor) { - setBackgroundColor(tab.backgroundColor, false); + fromTab = true; + if (overrideBackgroundColor = tab.overrideBackgroundColor) { + setBackgroundColor(tab.backgroundColor, true, false); } setActionBarColor(!tab.overrideActionBarColor ? Theme.getColor(tab.actionBarColorKey < 0 ? Theme.key_windowBackgroundWhite : tab.actionBarColorKey, resourcesProvider) : tab.actionBarColor, tab.overrideActionBarColor, false); + setNavigationBarColor(tab.navigationBarColor, false); showExpanded = tab.expanded; showOffsetY = tab.expandedOffset; webViewContainer.setIsBackButtonVisible(backButtonShown = tab.backButton); swipeContainer.setAllowSwipes(tab.allowSwipes); AndroidUtilities.updateImageViewImageAnimated(actionBar.getBackButton(), backButtonShown ? R.drawable.ic_ab_back : R.drawable.ic_close_white); + if (fullscreenButtons != null) { + fullscreenButtons.setBack(backButtonShown, false); + } needCloseConfirmation = tab.confirmDismiss; fullsize = tab.fullsize; needsContext = tab.needsContext; - if (tab.main != null) { - setMainButton(tab.main); + sensors = tab.sensors; + if (sensors != null) { + sensors.resume(); + } + if (tab.buttons != null) { +// setMainButton(tab.main); + botButtons.setState(tab.buttons, false); } + setFullscreen( tab.fullscreen, false); + currentAccount = tab.props != null ? tab.props.currentAccount : UserConfig.selectedAccount; if (tab.webView != null) { // tab.webView.resumeTimers(); tab.webView.onResume(); - webViewContainer.replaceWebView(tab.webView, tab.proxy); + webViewContainer.replaceWebView(currentAccount, tab.webView, tab.proxy); webViewContainer.setState(tab.ready || tab.webView.isPageLoaded(), tab.lastUrl); if (Theme.isCurrentThemeDark() != tab.themeIsDark) { // webViewContainer.notifyThemeChanged(); @@ -310,7 +392,7 @@ public boolean restoreState(BaseFragment fragment, BottomSheetTabs.WebTabData ta progressView.setVisibility(View.VISIBLE); webViewContainer.setBotUser(MessagesController.getInstance(currentAccount).getUser(botId)); - webViewContainer.loadFlickerAndSettingsItem(currentAccount, botId, settingsItem); + webViewContainer.loadFlickerAndSettingsItem(currentAccount, botId, null); webViewContainer.setState(false, null); if (webViewContainer.getWebView() != null) { webViewContainer.getWebView().loadUrl("about:blank"); @@ -324,9 +406,18 @@ public boolean restoreState(BaseFragment fragment, BottomSheetTabs.WebTabData ta tab.props.responseTime = 0; } requestWebView(fragment, tab.props); - if (settingsItem != null) { - settingsItem.setVisibility(tab.settings ? View.VISIBLE : View.GONE); + hasSettings = tab.settings; + + if (tab.error) { + errorShown = true; + createErrorContainer(); + errorContainer.set(UserObject.getUserName(MessagesController.getInstance(currentAccount).getUser(botId)), errorCode = tab.errorDescription); + errorContainer.setDark(AndroidUtilities.computePerceivedBrightness(backgroundPaint.getColor()) <= .721f, false); + errorContainer.setBackgroundColor(backgroundPaint.getColor()); + errorContainer.setVisibility(View.VISIBLE); + errorContainer.setAlpha(1f); } + lockOrientation(tab.orientationLocked); return true; } @@ -350,16 +441,29 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { padding = 0; } - if (getOffsetY() != padding && !dismissed) { + if (getOffsetY() != padding && !dismissed && resetOffsetY) { ignoreLayout = true; setOffsetY(padding); ignoreLayout = false; + resetOffsetY = false; } - if (AndroidUtilities.isTablet() && !AndroidUtilities.isInMultiwindow && !AndroidUtilities.isSmallTablet()) { + if (!fullscreen && AndroidUtilities.isTablet() && !AndroidUtilities.isInMultiwindow && !AndroidUtilities.isSmallTablet()) { widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.8f), MeasureSpec.EXACTLY); } - super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec) - ActionBar.getCurrentActionBarHeight() - AndroidUtilities.statusBarHeight + AndroidUtilities.dp(24) - (mainButtonWasVisible ? mainButton.getLayoutParams().height : 0), MeasureSpec.EXACTLY)); + int height = MeasureSpec.getSize(heightMeasureSpec); + if (!fullscreen) { + height -= AndroidUtilities.statusBarHeight; + height -= ActionBar.getCurrentActionBarHeight(); + } + if (botButtons != null && botButtons.getTotalHeight() > 0) { + height -= botButtons.getTotalHeight(); +// if (fullscreen) { +// height -= insets.bottom; +// } + } + height += dp(24); + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); } @Override @@ -369,14 +473,50 @@ public void requestLayout() { } super.requestLayout(); } + + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + if (fullscreenButtons != null) { + fullscreenButtons.setTranslationY(dp(24) + translationY); + } + if (bulletinContainer != null) { + bulletinContainer.setTranslationY(lerp(ActionBar.getCurrentActionBarHeight() - dp(24), insets.top + dp(24 + 8 + 30 + 8), fullscreenProgress) + swipeContainer.getTranslationY()); + } + } }; swipeContainer.setAllowFullSizeSwipe(true); swipeContainer.setShouldWaitWebViewScroll(true); webViewContainer = new BotWebViewContainer(context, resourcesProvider, getColor(Theme.key_windowBackgroundWhite), true) { @Override - public void onWebViewCreated() { - super.onWebViewCreated(); - swipeContainer.setWebView(webViewContainer.getWebView()); + public void onWebViewCreated(MyWebView webView) { + super.onWebViewCreated(webView); + swipeContainer.setWebView(webView); + if (sensors != null) { + sensors.attachWebView(webView); + } + fullscreenButtons.setWebView(webView); + } + + @Override + public void onWebViewDestroyed(MyWebView webView) { + if (sensors != null) { + sensors.detachWebView(webView); + } + fullscreenButtons.setWebView(null); + } + + @Override + protected void onErrorShown(boolean shown, int errorCode, String description) { + if (shown) { + createErrorContainer(); + errorContainer.set(UserObject.getUserName(MessagesController.getInstance(currentAccount).getUser(botId)), description); + errorContainer.setDark(AndroidUtilities.computePerceivedBrightness(backgroundPaint.getColor()) <= .721f, false); + errorContainer.setBackgroundColor(backgroundPaint.getColor()); + BotWebViewSheet.this.errorCode = description; + } + AndroidUtilities.updateViewVisibilityAnimated(errorContainer, errorShown = shown, 1f, false); + invalidate(); } }; webViewContainer.setDelegate(new BotWebViewContainer.Delegate() { @@ -394,7 +534,41 @@ public void onWebAppSetupClosingBehavior(boolean needConfirmation) { @Override public void onWebAppSwipingBehavior(boolean allowSwiping) { - swipeContainer.setAllowSwipes(allowSwiping); + if (swipeContainer != null) { + swipeContainer.setAllowSwipes(allowSwiping); + } + } + + @Override + public void onCloseToTabs() { + dismiss(true); + } + + @Override + public void onSharedTo(ArrayList dialogIds) { + String message; + if (dialogIds.size() == 1) { + message = LocaleController.formatString(R.string.BotSharedToOne, MessagesController.getInstance(currentAccount).getPeerName(dialogIds.get(0))); + } else { + message = LocaleController.formatPluralString("BotSharedToMany", dialogIds.size()); + } + showBulletin(b -> b.createSimpleBulletin(R.raw.forward, AndroidUtilities.replaceTags(message))); + } + + @Override + public void onOrientationLockChanged(boolean locked) { + lockOrientation(locked); + } + + @Override + public void onOpenBackFromTabs() { + if (lastTab != null) { + final BottomSheetTabs tabs = LaunchActivity.instance.getBottomSheetTabs(); + if (tabs != null) { + tabs.openTab(lastTab); + } + lastTab = null; + } } @Override @@ -423,21 +597,90 @@ public void onWebAppSetActionBarColor(int colorKey, int color, boolean isOverrid setActionBarColor(color, isOverrideColor, true); } + @Override + public void onWebAppSetNavigationBarColor(int color) { + setNavigationBarColor(color, true); + } + @Override public void onWebAppSetBackgroundColor(int color) { - setBackgroundColor(color, true); + setBackgroundColor(color, true, true); + } + + @Override + public void onLocationGranted(boolean granted) { + final TLRPC.User bot = MessagesController.getInstance(currentAccount).getUser(botId); + if (granted) { + BulletinFactory.UndoObject undo = new BulletinFactory.UndoObject(); + undo.undoText = LocaleController.getString(R.string.Undo); + undo.onUndo = () -> { + BotLocation.get(getContext(), currentAccount, botId).setGranted(false, null); + }; + showBulletin(b -> + b + .createUsersBulletin(Arrays.asList(bot), AndroidUtilities.replaceTags(LocaleController.formatString(R.string.BotLocationPermissionRequestGranted, UserObject.getUserName(bot))), null, undo) + .setDuration(DURATION_PROLONG) + ); + } else { + SpannableStringBuilder text = new SpannableStringBuilder(); + text.append(AndroidUtilities.replaceTags(LocaleController.formatString(R.string.BotLocationPermissionRequestDeniedApp, UserObject.getUserName(bot)))); + text.append(" "); + text.append(AndroidUtilities.replaceArrows(AndroidUtilities.makeClickable(LocaleController.getString(R.string.BotLocationPermissionRequestDeniedAppSettings), () -> { + BaseFragment lastFragment = LaunchActivity.getSafeLastFragment(); + if (lastFragment == null || lastFragment.getParentLayout() == null) return; + final INavigationLayout parentLayout = lastFragment.getParentLayout(); + lastFragment.presentFragment(ProfileActivity.of(botId)); + AndroidUtilities.scrollToFragmentRow(parentLayout, "botPermissionLocation"); + dismiss(true); + }), true)); + showBulletin(b -> + b.createSimpleBulletinDetail(R.raw.error, text) + .setDuration(DURATION_PROLONG) + ); + } + } + + @Override + public void onEmojiStatusGranted(boolean granted) { + final TLRPC.User bot = MessagesController.getInstance(currentAccount).getUser(botId); + if (granted) { + BulletinFactory.UndoObject undo = new BulletinFactory.UndoObject(); + undo.onUndo = () -> { + TL_bots.toggleUserEmojiStatusPermission req = new TL_bots.toggleUserEmojiStatusPermission(); + req.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); + req.enabled = false; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (res instanceof TLRPC.TL_boolTrue) { + webViewContainer.notifyEmojiStatusAccess("cancelled"); + } else { + showBulletin(b -> b.makeForError(err)); + } + })); + }; + showBulletin(b -> + b + .createUsersBulletin(Arrays.asList(bot), AndroidUtilities.replaceTags(LocaleController.formatString(R.string.BotEmojiStatusPermissionRequestGranted, UserObject.getUserName(bot))), null, undo) + .setDuration(DURATION_PROLONG) + ); + } + } + + @Override + public void onEmojiStatusSet(TLRPC.Document document) { + showBulletin(b -> b.createEmojiBulletin(document, LocaleController.getString(R.string.BotEmojiStatusUpdated))); } @Override public void onSetBackButtonVisible(boolean visible) { AndroidUtilities.updateImageViewImageAnimated(actionBar.getBackButton(), (backButtonShown = visible) ? R.drawable.ic_ab_back : R.drawable.ic_close_white); + if (fullscreenButtons != null) { + fullscreenButtons.setBack(visible, true); + } } @Override public void onSetSettingsButtonVisible(boolean visible) { - if (settingsItem != null) { - settingsItem.setVisibility(visible ? View.VISIBLE : View.GONE); - } + hasSettings = visible; } @Override @@ -538,7 +781,10 @@ public void onWebAppSwitchInlineQuery(TLRPC.User botUser, String query, List webViewContainer.onMainButtonPressed()); - windowView.addView(mainButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL)); - mainButtonAutoAnimator = VerticalPositionAutoAnimator.attach(mainButton); - - radialProgressView = new RadialProgressView(context) { - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) getLayoutParams(); - if (AndroidUtilities.isTablet() && !AndroidUtilities.isInMultiwindow && !AndroidUtilities.isSmallTablet()) { - params.rightMargin = (int) (AndroidUtilities.dp(10) + Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.1f); + botButtons.setOnButtonClickListener(main -> { + if (webViewContainer != null) { + if (main) { + webViewContainer.onMainButtonPressed(); } else { - params.rightMargin = AndroidUtilities.dp(10); + webViewContainer.onSecondaryButtonPressed(); } } - }; - radialProgressView.setSize(AndroidUtilities.dp(18)); - radialProgressView.setAlpha(0f); - radialProgressView.setScaleX(0.1f); - radialProgressView.setScaleY(0.1f); - radialProgressView.setVisibility(View.GONE); - windowView.addView(radialProgressView, LayoutHelper.createFrame(28, 28, Gravity.BOTTOM | Gravity.RIGHT, 0, 0, 10, 10)); - radialProgressAutoAnimator = VerticalPositionAutoAnimator.attach(radialProgressView); + }); + botButtons.setOnResizeListener(() -> { + swipeContainer.requestLayout(); + }); + windowView.addView(botButtons, botButtonsLayoutParams = LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL)); + + fullscreenButtons = new BotFullscreenButtons(getContext()); + fullscreenButtons.setAlpha(0f); + fullscreenButtons.setVisibility(View.GONE); + if (!MessagesController.getInstance(currentAccount).disableBotFullscreenBlur) { + fullscreenButtons.setParentRenderNode(swipeContainer.getRenderNode()); + } + windowView.addView(fullscreenButtons, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + fullscreenButtons.setOnCloseClickListener(() -> { + if (!webViewContainer.onBackPressed()) { + onCheckDismissByUser(); + } + }); + fullscreenButtons.setOnCollapseClickListener(() -> { + forceExpnaded = true; + dismiss(true, null); + }); + fullscreenButtons.setOnMenuClickListener(this::openOptions); + + bulletinContainer = new FrameLayout(context); + windowView.addView(bulletinContainer, bulletinContainerLayoutParams = LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 200, Gravity.TOP | Gravity.FILL_HORIZONTAL)); actionBarShadow = ContextCompat.getDrawable(getContext(), R.drawable.header_shadow).mutate(); @@ -655,7 +936,7 @@ public void onItemClick(int id) { } }); actionBar.setAlpha(0f); - windowView.addView(actionBar, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL)); + windowView.addView(actionBar, actionBarLayoutParams = LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL)); windowView.addView(progressView = new ChatAttachAlertBotWebViewLayout.WebProgressView(context, resourcesProvider) { @Override @@ -700,28 +981,31 @@ public void onAnimationEnd(Animator animation) { springAnimation.start(); } } - float offsetY = Math.max(0, swipeContainer.getSwipeOffsetY()); - mainButtonAutoAnimator.setOffsetY(offsetY); - radialProgressAutoAnimator.setOffsetY(offsetY); + float offsetY = fullscreen ? insets.bottom : Math.max(0, swipeContainer.getSwipeOffsetY()); lastSwipeTime = System.currentTimeMillis(); }); swipeContainer.setScrollEndListener(()-> webViewContainer.invalidateViewPortHeight(true)); - swipeContainer.setDelegate(() -> { -// if (can_minimize) { - dismiss(true, null); -// } else { -// if (!onCheckDismissByUser()) { -// swipeContainer.stickTo(0); -// } -// } + swipeContainer.setDelegate(byTap -> { + if (fullscreen && byTap) return; + dismiss(true, null); }); - swipeContainer.setTopActionBarOffsetY(ActionBar.getCurrentActionBarHeight() + AndroidUtilities.statusBarHeight - AndroidUtilities.dp(24)); swipeContainer.setIsKeyboardVisible(obj -> windowView.getKeyboardHeight() >= AndroidUtilities.dp(20)); passcodeView = new PasscodeView(context); windowView.addView(passcodeView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); setContentView(windowView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + updateFullscreenLayout(); + + bottomTabs = LaunchActivity.instance != null ? LaunchActivity.instance.getBottomSheetTabs() : null; + if (bottomTabs != null) { + bottomTabs.listen(windowView::invalidate, this::relayout); + bottomTabsClip = new BottomSheetTabs.ClipTools(bottomTabs); + } + } + + private void relayout() { + updateFullscreenLayout(); } @Override @@ -755,7 +1039,7 @@ public void setParentActivity(Activity parentActivity) { } private void updateActionBarColors() { - if (!overrideBackgroundColor) { + if (!overrideActionBarColor) { actionBar.setTitleColor(getColor(Theme.key_windowBackgroundWhiteBlackText)); actionBar.setItemsColor(getColor(Theme.key_windowBackgroundWhiteBlackText), false); actionBar.setItemsBackgroundColor(getColor(Theme.key_actionBarWhiteSelector), false); @@ -764,11 +1048,12 @@ private void updateActionBarColors() { actionBar.setPopupItemsColor(getColor(Theme.key_actionBarDefaultSubmenuItemIcon), true, false); actionBar.setPopupItemsSelectorColor(getColor(Theme.key_dialogButtonSelector), false); } + webViewContainer.setFlickerViewColor(backgroundPaint.getColor()); } private void updateLightStatusBar() { boolean lightStatusBar; - if (overrideBackgroundColor) { + if (overrideActionBarColor) { lightStatusBar = !actionBarIsLight; } else { int color = Theme.getColor(Theme.key_windowBackgroundWhite, null, true); @@ -812,6 +1097,11 @@ protected void onCreate(Bundle savedInstanceState) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { params.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; } + if (fullscreen) { + params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; + } else { + params.flags &=~ WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; + } window.setAttributes(params); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { @@ -822,7 +1112,24 @@ protected void onCreate(Bundle savedInstanceState) { windowView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { windowView.setOnApplyWindowInsetsListener((v, insets) -> { - v.setPadding(0, 0, 0, insets.getSystemWindowInsetBottom()); + final WindowInsetsCompat insetsCompat = WindowInsetsCompat.toWindowInsetsCompat(insets, v); + final androidx.core.graphics.Insets navInsets = insetsCompat.getInsets(WindowInsetsCompat.Type.navigationBars()); + this.navInsets.set(navInsets.left, navInsets.top, navInsets.right, navInsets.bottom); + final androidx.core.graphics.Insets cutoutInsets = insetsCompat.getInsets(WindowInsetsCompat.Type.displayCutout() | WindowInsetsCompat.Type.statusBars() | WindowInsetsCompat.Type.navigationBars()); + this.insets.set( + Math.max(cutoutInsets.left, insets.getStableInsetLeft()), + Math.max(cutoutInsets.top, insets.getStableInsetTop()), + Math.max(cutoutInsets.right, insets.getStableInsetRight()), + Math.max(cutoutInsets.bottom, insets.getStableInsetBottom()) + ); + final androidx.core.graphics.Insets keyboardInsets = insetsCompat.getInsets(WindowInsetsCompat.Type.ime()); + final int keyboardHeight = keyboardInsets.bottom; + if (keyboardHeight > this.insets.bottom && keyboardHeight > dp(20)) { + this.keyboardInset = keyboardHeight; + } else { + this.keyboardInset = 0; + } + updateFullscreenLayout(); if (Build.VERSION.SDK_INT >= 30) { return WindowInsets.CONSUMED; } else { @@ -830,18 +1137,83 @@ protected void onCreate(Bundle savedInstanceState) { } }); } + if (fullscreen && !(botButtons != null && botButtons.getTotalHeight() > 0)) { + windowView.setSystemUiVisibility(windowView.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + } else { + windowView.setSystemUiVisibility(windowView.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { AndroidUtilities.setLightNavigationBar(window, ColorUtils.calculateLuminance(navBarColor) >= 0.721f); } NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.didSetNewTheme); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.botDownloadsUpdate); + } + + public void updateFullscreenLayout() { + fullscreenButtons.setInsets(insets); + if (fullscreen) { + final int bottom = botButtons != null && botButtons.getTotalHeight() > 0 ? insets.bottom : 0; + webViewContainer.reportSafeInsets(new Rect(insets.left, insets.top, insets.right, keyboardInset > bottom ? 0 : (botButtons != null && botButtons.getTotalHeight() > 0 ? 0 : insets.bottom)), dp(8 + 30 + 8)); + windowView.setPadding(0, 0, 0, Math.max(keyboardInset, bottom)); + } else { + webViewContainer.reportSafeInsets(new Rect(0, 0, 0, 0), 0); + windowView.setPadding(insets.left, 0, insets.right, Math.max(this.keyboardInset, (bottomTabs != null ? bottomTabs.getHeight(false) : 0) + insets.bottom)); + } + swipeContainerLayoutParams.topMargin = dp(24); +// botButtonsLayoutParams.bottomMargin = fullscreen ? insets.bottom : 0; + actionBarLayoutParams.leftMargin = !fullscreen ? 0 : insets.left; + actionBarLayoutParams.rightMargin = 0; + bulletinContainerLayoutParams.leftMargin = !fullscreen ? 0 : insets.left; + bulletinContainerLayoutParams.rightMargin = !fullscreen ? 0 : insets.right; + if (!fullscreenInProgress) { + swipeContainer.setSwipeOffsetAnimationDisallowed(true); + if (fullscreen) { + swipeContainer.setTopActionBarOffsetY(-dp(24)); + } else { + swipeContainer.setTopActionBarOffsetY(ActionBar.getCurrentActionBarHeight() + AndroidUtilities.statusBarHeight - dp(24)); + } + swipeContainer.setSwipeOffsetAnimationDisallowed(false); + swipeContainer.invalidateTranslation(); + swipeContainer.invalidate(); + swipeContainer.requestLayout(); + } + if (swipeContainer != null) { + swipeContainer.setFullSize(isFullSize()); + } + botButtons.requestLayout(); + windowView.requestLayout(); + fullscreenButtons.setVisibility(fullscreen ? View.VISIBLE : View.GONE); + } + + public void updateWindowFlags() { + try { + Window window = getWindow(); + if (window == null) return; + WindowManager.LayoutParams params = window.getAttributes(); + if (fullscreen) { + params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; + } else { + params.flags &= ~WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; + } + if (fullscreen && !(botButtons != null && botButtons.getTotalHeight() > 0) && !windowView.drawingFromOverlay) { + windowView.setSystemUiVisibility(windowView.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + } else { + windowView.setSystemUiVisibility(windowView.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + } + window.setAttributes(params); + } catch (Exception e) { + FileLog.e(e); + } } @Override public void onAttachedToWindow() { super.onAttachedToWindow(); + setAttached(true); + if (springAnimation == null) { springAnimation = new SpringAnimation(this, ACTION_BAR_TRANSITION_PROGRESS_VALUE) .setSpring(new SpringForce() @@ -855,6 +1227,8 @@ public void onAttachedToWindow() { public void onDetachedFromWindow() { super.onDetachedFromWindow(); + setAttached(false); + if (springAnimation != null) { springAnimation.cancel(); springAnimation = null; @@ -914,7 +1288,7 @@ public void setNeedsContext(boolean needsContext) { } public boolean isFullSize() { - return fullsize == null ? defaultFullsize : fullsize; + return fullscreen || (fullsize == null ? defaultFullsize : fullsize); } @Override @@ -922,6 +1296,11 @@ public boolean setDialog(BottomSheetTabDialog dialog) { return false; } + @Override + public boolean hadDialog() { + return false; + } + Drawable verifiedDrawable; public void requestWebView(BaseFragment fragment, WebViewRequestProps props) { @@ -934,7 +1313,7 @@ public void requestWebView(BaseFragment fragment, WebViewRequestProps props) { this.buttonText = props.buttonText; this.currentWebApp = props.app; - TLRPC.User userbot = MessagesController.getInstance(currentAccount).getUser(botId); + final TLRPC.User userbot = MessagesController.getInstance(currentAccount).getUser(botId); CharSequence title = UserObject.getUserName(userbot); try { TextPaint tp = new TextPaint(); @@ -942,7 +1321,7 @@ public void requestWebView(BaseFragment fragment, WebViewRequestProps props) { title = Emoji.replaceEmoji(title, tp.getFontMetricsInt(), false); } catch (Exception ignore) {} actionBar.setTitle(title); - TLRPC.UserFull userInfo = MessagesController.getInstance(currentAccount).getUserFull(botId); + final TLRPC.UserFull userInfo = MessagesController.getInstance(currentAccount).getUserFull(botId); if (userbot != null && userbot.verified || userInfo != null && userInfo.user != null && userInfo.user.verified) { verifiedDrawable = getContext().getResources().getDrawable(R.drawable.verified_profile).mutate(); verifiedDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_featuredStickers_addButton), PorterDuff.Mode.SRC_IN)); @@ -980,6 +1359,9 @@ public int getIntrinsicWidth() { } }); } + if (fullscreenButtons != null) { + fullscreenButtons.setName(UserObject.getUserName(userbot), userbot != null && userbot.verified); + } ActionBarMenu menu = actionBar.createMenu(); menu.removeAllViews(); @@ -990,20 +1372,30 @@ public int getIntrinsicWidth() { break; } } + if (!fromTab) { + if (userInfo != null) { + if (userInfo.bot_info != null && userInfo.bot_info.app_settings != null) { + applyAppBotSettings(userInfo.bot_info.app_settings, false); + } + } else { + MessagesController.getInstance(currentAccount).loadFullUser(userbot, 0, true, (userFull2) -> { + AndroidUtilities.runOnUIThread(() -> { + if (userFull2 != null && userFull2.bot_info != null && userFull2.bot_info.app_settings != null) { + applyAppBotSettings(userFull2.bot_info.app_settings, true); + } + }); + }); + } + if (props.fullscreen) { + setFullscreen(true, false); + } + } menu.addItem(R.id.menu_collapse_bot, R.drawable.arrow_more); - 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)); - settingsItem.setVisibility(View.GONE); - otherItem.addSubItem(R.id.menu_reload_page, R.drawable.msg_retry, LocaleController.getString(R.string.BotWebViewReloadPage)); - if (currentBot != null && MediaDataController.getInstance(currentAccount).canCreateAttachedMenuBotShortcut(currentBot.bot_id)) { - otherItem.addSubItem(R.id.menu_add_to_home_screen_bot, R.drawable.msg_home, LocaleController.getString(R.string.AddShortcut)); - } - otherItem.addSubItem(R.id.menu_tos_bot, R.drawable.menu_intro, LocaleController.getString(R.string.BotWebViewToS)); - 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)); - } + optionsItem = menu.addItem(0, optionsIcon = new BotFullscreenButtons.OptionsIcon(getContext())); + optionsItem.setOnClickListener(v -> { + openOptions(); + }); actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override @@ -1012,34 +1404,6 @@ public void onItemClick(int id) { if (!webViewContainer.onBackPressed()) { onCheckDismissByUser(); } - } else if (id == R.id.menu_open_bot) { - Bundle bundle = new Bundle(); - bundle.putLong("user_id", botId); - if (parentActivity instanceof LaunchActivity) { - ((LaunchActivity) parentActivity).presentFragment(new ChatActivity(bundle)); - } - dismiss(); - } else if (id == R.id.menu_tos_bot) { - Browser.openUrl(getContext(), LocaleController.getString(R.string.BotWebViewToSLink)); - } else if (id == R.id.menu_reload_page) { - if (webViewContainer.getWebView() != null) { - webViewContainer.getWebView().animate().cancel(); - webViewContainer.getWebView().animate().alpha(0).start(); - } - - progressView.setLoadProgress(0); - progressView.setAlpha(1f); - progressView.setVisibility(View.VISIBLE); - - webViewContainer.setBotUser(MessagesController.getInstance(currentAccount).getUser(botId)); - webViewContainer.loadFlickerAndSettingsItem(currentAccount, botId, settingsItem); - webViewContainer.reload(); - } else if (id == R.id.menu_settings) { - webViewContainer.onSettingsButtonPressed(); - } else if (id == R.id.menu_delete_bot) { - deleteBot(currentAccount, botId, () -> dismiss()); - } else if (id == R.id.menu_add_to_home_screen_bot) { - MediaDataController.getInstance(currentAccount).installShortcut(botId, MediaDataController.SHORTCUT_TYPE_ATTACHED_BOT); } else if (id == R.id.menu_collapse_bot) { forceExpnaded = true; dismiss(true, null); @@ -1050,10 +1414,10 @@ public void onItemClick(int id) { final JSONObject themeParams = makeThemeParams(resourcesProvider); webViewContainer.setBotUser(MessagesController.getInstance(currentAccount).getUser(botId)); - webViewContainer.loadFlickerAndSettingsItem(currentAccount, botId, settingsItem); + webViewContainer.loadFlickerAndSettingsItem(currentAccount, botId, null); preloadShortcutBotIcon(props.botUser, currentBot); if (props.response != null) { - loadFromResponse(true); + loadFromResponse(); } else { switch (props.type) { case TYPE_BOT_MENU_BUTTON: { @@ -1062,6 +1426,7 @@ public void onItemClick(int id) { req.peer = MessagesController.getInstance(currentAccount).getInputPeer(botId); req.platform = "android"; req.compact = props.compact; + req.fullscreen = props.fullscreen; req.url = props.buttonUrl; req.flags |= 2; @@ -1077,7 +1442,7 @@ public void onItemClick(int id) { } else if (requestProps != null) { requestProps.applyResponse(response); - loadFromResponse(false); + loadFromResponse(); } })); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.webViewResultSent); @@ -1091,6 +1456,7 @@ public void onItemClick(int id) { req.platform = "android"; req.from_side_menu = (props.flags & FLAG_FROM_SIDE_MENU) != 0; req.compact = props.compact; + req.fullscreen = props.fullscreen; if (themeParams != null) { req.theme_params = new TLRPC.TL_dataJSON(); req.theme_params.data = themeParams.toString(); @@ -1110,7 +1476,7 @@ public void onItemClick(int id) { } else if (requestProps != null) { requestProps.applyResponse(response); - loadFromResponse(false); + loadFromResponse(); } })); break; @@ -1121,6 +1487,7 @@ public void onItemClick(int id) { req.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); req.platform = "android"; req.compact = props.compact; + req.fullscreen = props.fullscreen; if (props.buttonUrl != null) { req.url = props.buttonUrl; req.flags |= 2; @@ -1142,7 +1509,7 @@ public void onItemClick(int id) { } else if (requestProps != null) { requestProps.applyResponse(response); - loadFromResponse(false); + loadFromResponse(); } })); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.webViewResultSent); @@ -1160,6 +1527,7 @@ public void onItemClick(int id) { req.peer = fragment instanceof ChatActivity ? ((ChatActivity) fragment).getCurrentUser() != null ? MessagesController.getInputPeer(((ChatActivity) fragment).getCurrentUser()) : MessagesController.getInputPeer(((ChatActivity) fragment).getCurrentChat()) : MessagesController.getInputPeer(props.botUser); req.compact = props.compact; + req.fullscreen = props.fullscreen; if (!TextUtils.isEmpty(props.startParam)) { req.start_param = props.startParam; @@ -1177,7 +1545,7 @@ public void onItemClick(int id) { } else if (requestProps != null) { requestProps.applyResponse(response2); - loadFromResponse(false); + loadFromResponse(); } }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); break; @@ -1190,6 +1558,7 @@ public void onItemClick(int id) { req.peer = fragment instanceof ChatActivity ? ((ChatActivity) fragment).getCurrentUser() != null ? MessagesController.getInputPeer(((ChatActivity) fragment).getCurrentUser()) : MessagesController.getInputPeer(((ChatActivity) fragment).getCurrentChat()) : MessagesController.getInputPeer(props.botUser); req.compact = props.compact; + req.fullscreen = props.fullscreen; if (!TextUtils.isEmpty(props.startParam)) { req.start_param = props.startParam; @@ -1207,7 +1576,7 @@ public void onItemClick(int id) { } else if (requestProps != null) { requestProps.applyResponse(response2); - loadFromResponse(false); + loadFromResponse(); } }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); break; @@ -1216,7 +1585,186 @@ public void onItemClick(int id) { } } - private void loadFromResponse(boolean fromTab) { + private HashMap fileItems = new HashMap<>(); + private ItemOptions options; + private void openOptions() { + final TLRPC.User userbot = 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 (options != null) { + options.dismiss(); + } + final ItemOptions o = options = ItemOptions.makeOptions(windowView, resourcesProvider, fullscreen ? fullscreenButtons : optionsItem, true); + final BotDownloads botDownloads = BotDownloads.get(getContext(), currentAccount, botId); + fileItems.clear(); + if (botDownloads.hasFiles()) { + final ItemOptions so = o.makeSwipeback(); + so.add(R.drawable.msg_arrow_back, LocaleController.getString(R.string.Back), o::closeSwipeback); + so.addGap(); + for (BotDownloads.FileDownload file : botDownloads.getFiles()) { + final ActionBarMenuSubItem fileItem = so.add(file.file_name, "", () -> {}).getLast(); + fileItems.put(file, fileItem); + } + updateDownloadBulletin(); + so.setMinWidth(dp(180)); + + o.add(R.drawable.menu_download_round, LocaleController.getString(R.string.BotDownloads), () -> { + o.openSwipeback(so); + }); + o.addGap(); + } + o + .add(R.drawable.msg_bot, LocaleController.getString(R.string.BotWebViewOpenBot), () -> { + if (parentActivity instanceof LaunchActivity) { + ((LaunchActivity) parentActivity).presentFragment(ChatActivity.of(botId)); + } + dismiss(true); + }) + .addIf(hasSettings, R.drawable.msg_settings, LocaleController.getString(R.string.BotWebViewSettings), () -> { + webViewContainer.onSettingsButtonPressed(); + }) + .add(R.drawable.msg_retry, LocaleController.getString(R.string.BotWebViewReloadPage), () -> { + if (webViewContainer.getWebView() != null) { + webViewContainer.getWebView().animate().cancel(); + webViewContainer.getWebView().animate().alpha(0).start(); + } + + progressView.setLoadProgress(0); + progressView.setAlpha(1f); + progressView.setVisibility(View.VISIBLE); + + webViewContainer.setBotUser(MessagesController.getInstance(currentAccount).getUser(botId)); + webViewContainer.loadFlickerAndSettingsItem(currentAccount, botId, null); + webViewContainer.reload(); + }) + .addIf(userbot != null && userbot.bot_has_main_app, R.drawable.msg_home, LocaleController.getString(R.string.AddShortcut), () -> { + MediaDataController.getInstance(currentAccount).installShortcut(botId, MediaDataController.SHORTCUT_TYPE_ATTACHED_BOT); + }) + .add(R.drawable.menu_intro, LocaleController.getString(R.string.BotWebViewToS), () -> { + Browser.openUrl(getContext(), LocaleController.getString(R.string.BotWebViewToSLink)); + }) + .addIf(currentBot != null && (currentBot.show_in_side_menu || currentBot.show_in_attach_menu), R.drawable.msg_delete, LocaleController.getString(R.string.BotWebViewDeleteBot), () -> { + deleteBot(currentAccount, botId, () -> dismiss()); + }) + .setGravity(Gravity.RIGHT) + .translate(-insets.right, 0) + .forceTop(true) + .setDrawScrim(false) + .show(); + } + + private void showBulletin(Utilities.CallbackReturn make) { + make.run(BulletinFactory.of(bulletinContainer, resourcesProvider)).show(true); + } + + private void updateDownloadBulletinArrow() { + if (downloadBulletinLayout == null) return; + if (fullscreen) { + downloadBulletinLayout.setArrow(lerp(dp(24), dp(26), fullscreenProgress)); + } else if (actionBarTransitionProgress > .5f) { + downloadBulletinLayout.setArrow(dp(24)); + } else { + downloadBulletinLayout.setArrow(-1); + } + } + + private BotDownloads.FileDownload lastBulletinFile; + private void updateDownloadBulletin() { + final BotDownloads botDownloads = BotDownloads.get(getContext(), currentAccount, botId); + final BotDownloads.FileDownload file = botDownloads.getCurrent(); + + if (file == null) { + if (downloadBulletin != null) { + downloadBulletin.hide(); + downloadBulletin = null; + } + } else if (file.isDownloading() && !file.shown || file.resaved) { + if (lastBulletinFile != file && downloadBulletin != null) { + downloadBulletin.hide(); + downloadBulletin = null; + } + if (downloadBulletin == null || !downloadBulletin.isShowing()) { + lastBulletinFile = file; + downloadBulletin = Bulletin.make(bulletinContainer, downloadBulletinLayout = new BotDownloads.DownloadBulletin(getContext(), resourcesProvider), DURATION_PROLONG); + downloadBulletin.show(true); + } + if (downloadBulletinLayout.set(file)) { + downloadBulletin = null; + } + file.resaved = false; + file.shown = true; + } else if (downloadBulletinLayout != null) { + lastBulletinFile = file; + if (downloadBulletinLayout.set(file)) { + downloadBulletin = null; + } + } + updateDownloadBulletinArrow(); + + for (Map.Entry entry : fileItems.entrySet()) { + final ActionBarMenuSubItem item = entry.getValue(); + final BotDownloads.FileDownload itemFile = entry.getKey(); + + item.setText(itemFile.file_name); + if (!itemFile.isDownloading()) { + item.setSubtext(AndroidUtilities.formatFileSize(itemFile.size)); + } else { + final Pair progress = itemFile.getProgress(); + if (progress.second > 0) { + item.setSubtext(AndroidUtilities.formatFileSize(progress.first) + " / " + AndroidUtilities.formatFileSize(progress.second)); + } else { + item.setSubtext(AndroidUtilities.formatFileSize(progress.first)); + } + } + + if (itemFile.isDownloading()) { + item.setRightIcon(R.drawable.msg_close); + item.subtextView.setPadding(0, 0, dp(32), 0); + } else if (itemFile.cancelled) { + item.setVisibility(View.GONE); + } else { + item.setRightIcon(0); + item.subtextView.setPadding(0, 0, 0, 0); + } + + item.setOnClickListener(v -> { + if (itemFile.isDownloading()) { + itemFile.cancel(); + } else { + itemFile.open(); + } + if (options != null) { + options.dismiss(); + options = null; + } + }); + } + + optionsIcon.setDownloading(botDownloads.isDownloading()); + fullscreenButtons.setDownloading(botDownloads.isDownloading()); + + } + + private void applyAppBotSettings(TL_bots.botAppSettings botAppSettings, boolean animated) { + if (botAppSettings == null) return; + final boolean dark = Theme.isCurrentThemeDark(); + final boolean hasBackgroundColor = (botAppSettings.flags & ((dark ? 4 : 2))) != 0; + final boolean hasHeaderColor = (botAppSettings.flags & ((dark ? 16 : 8))) != 0; + if (hasHeaderColor) { + setActionBarColor((dark ? botAppSettings.header_dark_color : botAppSettings.header_color) | 0xFF000000, true, animated); + } + if (hasBackgroundColor) { + setBackgroundColor((dark ? botAppSettings.background_dark_color : botAppSettings.background_color) | 0xFF000000, true, animated); + setNavigationBarColor((dark ? botAppSettings.background_dark_color : botAppSettings.background_color) | 0xFF000000, animated); + } + } + + private void loadFromResponse() { if (requestProps == null) return; final long pollTimeout = Math.max(0, POLL_PERIOD - (System.currentTimeMillis() - requestProps.responseTime)); String url = null; @@ -1226,6 +1774,9 @@ private void loadFromResponse(boolean fromTab) { queryId = resultUrl.query_id; url = resultUrl.url; fullsize = resultUrl.fullsize; + if (!fromTab) { + setFullscreen(resultUrl.fullscreen, !fromTab); + } } else if (requestProps.response instanceof TLRPC.TL_appWebViewResultUrl) { // deprecated TLRPC.TL_appWebViewResultUrl result = (TLRPC.TL_appWebViewResultUrl) requestProps.response; queryId = 0; @@ -1273,7 +1824,7 @@ public static void deleteBot(int currentAccount, long botId, Runnable onDone) { return; } String botName = currentBot.short_name; - description = LocaleController.formatString("BotRemoveFromMenu", R.string.BotRemoveFromMenu, botName); + description = LocaleController.formatString(R.string.BotRemoveFromMenu, botName); TLRPC.TL_attachMenuBot finalCurrentBot = currentBot; new AlertDialog.Builder(LaunchActivity.getLastFragment().getContext()) .setTitle(LocaleController.getString(R.string.BotRemoveFromMenuTitle)) @@ -1303,6 +1854,7 @@ private int getColor(int key) { @Override public void show() { if (!AndroidUtilities.isSafeToShow(getContext())) return; + setOpen(true); windowView.setAlpha(0f); windowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { @Override @@ -1312,15 +1864,16 @@ public void onLayoutChange(View v, int left, int top, int right, int bottom, int swipeContainer.setSwipeOffsetY(swipeContainer.getHeight()); windowView.setAlpha(1f); - AnimationNotificationsLocker locker = new AnimationNotificationsLocker(); - locker.lock(); - if (showOffsetY != Float.MAX_VALUE) { swipeContainer.setSwipeOffsetAnimationDisallowed(true); swipeContainer.setOffsetY(showOffsetY); swipeContainer.setSwipeOffsetAnimationDisallowed(false); } + webViewContainer.invalidateViewPortHeight(true, true); + AnimationNotificationsLocker locker = new AnimationNotificationsLocker(); + locker.lock(); + if (showExpanded || isFullSize()) { swipeContainer.stickTo(-swipeContainer.getOffsetY() + swipeContainer.getTopActionBarOffsetY(), locker::unlock); } else { @@ -1333,9 +1886,16 @@ public void onLayoutChange(View v, int left, int top, int right, int bottom, int }).start(); } swipeContainer.opened = true; + + if (fullscreen && fullscreenButtons != null) { + fullscreenButtons.setAlpha(0f); + fullscreenButtons.animate().alpha(1f).setDuration(220).start(); + } } }); super.show(); + superDismissed = false; + activeSheets.add(this); } @Override @@ -1398,14 +1958,17 @@ public void dismiss(Runnable callback) { dismiss(false, callback); } + private boolean superDismissed = false; public void dismiss(boolean intoTabs, Runnable callback) { if (dismissed) { return; } dismissed = true; + setOpen(false); AndroidUtilities.cancelRunOnUIThread(pollRunnable); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.webViewResultSent); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.botDownloadsUpdate); NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.didSetNewTheme); if (intoTabs && (LaunchActivity.instance == null || LaunchActivity.instance.getBottomSheetTabsOverlay() == null)) { @@ -1418,18 +1981,55 @@ public void dismiss(boolean intoTabs, Runnable callback) { } LaunchActivity.instance.getBottomSheetTabsOverlay().dismissSheet(this); } else { + if (botButtons != null) { + botButtons.animate().translationY(botButtons.getTotalHeight()).alpha(0).setDuration(160).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); + } webViewContainer.destroyWebView(); - swipeContainer.stickTo(swipeContainer.getHeight() + windowView.measureKeyboardHeight() + (isFullSize() ? dp(200) : 0), () -> { - super.dismiss(); + swipeContainer.stickTo(swipeContainer.getHeight() + (botButtons != null ? botButtons.getTotalHeight() : 0) + insets.top + insets.bottom + windowView.measureKeyboardHeight() + (isFullSize() ? dp(200) : 0), true, () -> { + if (!superDismissed) { + super.dismiss(); + superDismissed = true; + } if (callback != null) { callback.run(); } }); } + activeSheets.remove(this); } public void release() { - super.dismiss(); + if (superDismissed) return; + try { + super.dismiss(); + } catch (Exception e) { + FileLog.e(e); + } + setOpen(false); + } + + private float openedProgress; + private ValueAnimator openAnimator; + public void setOpen(boolean opened) { + if (openAnimator != null) { + openAnimator.cancel(); + } + if (Math.abs(openedProgress - (opened ? 1.0f : 0.0f)) < 0.01f) return; + openAnimator = ValueAnimator.ofFloat(openedProgress, opened ? 1.0f : 0.0f); + openAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + openedProgress = opened ? 1.0f : 0.0f; + checkNavBarColor(); + } + }); + openAnimator.addUpdateListener(anm -> { + openedProgress = (float) anm.getAnimatedValue(); + checkNavBarColor(); + }); + openAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + openAnimator.setDuration(220); + openAnimator.start(); } @Override @@ -1445,47 +2045,216 @@ public void didReceivedNotification(int id, int account, Object... args) { webViewContainer.updateFlickerBackgroundColor(getColor(Theme.key_windowBackgroundWhite)); updateActionBarColors(); updateLightStatusBar(); + } else if (id == NotificationCenter.botDownloadsUpdate) { + updateDownloadBulletin(); } } public static int navigationBarColor(int actionBarColor) { - final boolean isDark = AndroidUtilities.computePerceivedBrightness(actionBarColor) < 0.721f; -// final int themeNavBarColor = (Theme.isCurrentThemeDark() == isDark ? Theme.getColor(Theme.key_windowBackgroundGray) : (isDark ? 0xFF151E27 : 0xFFF0F0F0)); -// return Theme.adaptHue(themeNavBarColor, actionBarColor); -// return OKLCH.adapt(themeNavBarColor, actionBarColor); return Theme.adaptHSV(actionBarColor, +.35f, -.1f); } - - public void setBackgroundColor(int color, boolean animated) { + private ValueAnimator backgroundColorAnimator; + public void setBackgroundColor(int color, boolean isOverride, boolean animated) { int from = backgroundPaint.getColor(); + overrideBackgroundColor = isOverride; + if (backgroundColorAnimator != null) { + backgroundColorAnimator.cancel(); + } if (animated) { - ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(200); - animator.setInterpolator(CubicBezierInterpolator.DEFAULT); - animator.addUpdateListener(animation -> { + backgroundColorAnimator = ValueAnimator.ofFloat(0, 1).setDuration(200); + backgroundColorAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + backgroundColorAnimator.addUpdateListener(animation -> { backgroundPaint.setColor(ColorUtils.blendARGB(from, color, (Float) animation.getAnimatedValue())); updateActionBarColors(); windowView.invalidate(); + if (errorContainer != null) { + errorContainer.setDark(AndroidUtilities.computePerceivedBrightness(backgroundPaint.getColor()) <= .721f, false); + errorContainer.setBackgroundColor(backgroundPaint.getColor()); + } }); - animator.start(); + backgroundColorAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + backgroundPaint.setColor(color); + updateActionBarColors(); + windowView.invalidate(); + if (errorContainer != null) { + errorContainer.setDark(AndroidUtilities.computePerceivedBrightness(backgroundPaint.getColor()) <= .721f, false); + errorContainer.setBackgroundColor(backgroundPaint.getColor()); + } + } + }); + backgroundColorAnimator.start(); } else { backgroundPaint.setColor(color); updateActionBarColors(); windowView.invalidate(); + if (errorContainer != null) { + errorContainer.setDark(AndroidUtilities.computePerceivedBrightness(backgroundPaint.getColor()) <= .721f, false); + errorContainer.setBackgroundColor(backgroundPaint.getColor()); + } } } - public void setActionBarColor(int color, boolean isOverrideColor, boolean animated) { + private boolean resetOffsetY = true; + private ValueAnimator fullscreenAnimator; + public void setFullscreen(boolean fullscreen, boolean animated) { + if (this.fullscreen == fullscreen) return; + this.fullscreen = fullscreen; + if (fullscreenAnimator != null) { + fullscreenAnimator.cancel(); + } + if (fullscreenButtons != null) { + fullscreenButtons.setPreview(fullscreen, animated); + } + swipeContainerFromWidth = swipeContainer.getWidth(); + swipeContainerFromHeight = swipeContainer.getHeight(); + resetOffsetY = false; + if (animated) { + updateFullscreenLayout(); + updateWindowFlags(); + updateDownloadBulletinArrow(); + final float tabletOffset = AndroidUtilities.isTablet() && !AndroidUtilities.isInMultiwindow && !AndroidUtilities.isSmallTablet() ? (AndroidUtilities.displaySize.x - (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.8f)) / 2f : 0; + final float fromTranslationX = fullscreen ? insets.left + tabletOffset : -insets.left - tabletOffset; + final float fromButtonsTranslationX = fullscreen ? +tabletOffset : -tabletOffset; + final float toTranslationX = 0; + final float fromTranslationY = fullscreen ? swipeContainer.getTranslationY() : -dp(24); + final float toTranslationY = fullscreen ? -dp(24) : (ActionBar.getCurrentActionBarHeight() + AndroidUtilities.statusBarHeight - dp(24)); + final float topoffset = ActionBar.getCurrentActionBarHeight() + AndroidUtilities.statusBarHeight; + swipeContainer.cancelStickTo(); + swipeContainer.setSwipeOffsetAnimationDisallowed(true); + actionBar.setVisibility(View.VISIBLE); + if (fullscreen) { + swipeContainer.setTopActionBarOffsetY(-dp(24)); + } else { + swipeContainer.setTopActionBarOffsetY(topoffset - dp(24)); + } + swipeContainer.invalidateTranslation(); + swipeContainer.invalidate(); + + fullscreenTransitionProgress = 0.0f; + fullscreenProgress = fullscreen ? fullscreenTransitionProgress : 1.0f - fullscreenTransitionProgress; + actionBar.setAlpha(1.0f - fullscreenProgress); + actionBar.setTranslationY(-ActionBar.getCurrentActionBarHeight() * fullscreenProgress); + swipeContainer.setTranslationY(lerp(fromTranslationY, toTranslationY, fullscreenTransitionProgress)); + swipeContainer.setTranslationX(lerp(fromTranslationX, toTranslationX, fullscreenTransitionProgress)); + botButtons.setTranslationX(lerp(fromButtonsTranslationX, 0, fullscreenTransitionProgress)); + fullscreenButtons.setAlpha(fullscreenProgress); + windowView.invalidate(); + webViewContainer.setViewPortHeightOffset(swipeContainer.getTranslationY() - toTranslationY); + webViewContainer.invalidateViewPortHeight(false, false); + + fullscreenInProgress = true; + fullscreenAnimator = ValueAnimator.ofFloat(0, 1); + fullscreenAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(@NonNull ValueAnimator animation) { + fullscreenTransitionProgress = (float) animation.getAnimatedValue(); + fullscreenProgress = fullscreen ? fullscreenTransitionProgress : 1.0f - fullscreenTransitionProgress; + actionBar.setAlpha(1.0f - fullscreenProgress); + actionBar.setTranslationY(-ActionBar.getCurrentActionBarHeight() * fullscreenProgress); + swipeContainer.setTranslationY(lerp(fromTranslationY, toTranslationY, fullscreenTransitionProgress)); + swipeContainer.setTranslationX(lerp(fromTranslationX, toTranslationX, fullscreenTransitionProgress)); + botButtons.setTranslationX(lerp(fromButtonsTranslationX, 0, fullscreenTransitionProgress)); + fullscreenButtons.setAlpha(fullscreenProgress); + windowView.invalidate(); + webViewContainer.setViewPortHeightOffset(swipeContainer.getTranslationY() - toTranslationY); + webViewContainer.invalidateViewPortHeight(false, false); + updateDownloadBulletinArrow(); + } + }); + fullscreenAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + fullscreenInProgress = false; + if (!fullscreen) { + updateFullscreenLayout(); + updateWindowFlags(); + swipeContainer.setForceOffsetY(topoffset - dp(24)); + swipeContainer.setTopActionBarOffsetY(topoffset - dp(24)); + swipeContainer.setSwipeOffsetY(0); + } else { + swipeContainer.setForceOffsetY(-dp(24)); + swipeContainer.setTopActionBarOffsetY(-dp(24)); + swipeContainer.setSwipeOffsetY(0); + } + fullscreenProgress = fullscreen ? fullscreenTransitionProgress : 1.0f - fullscreenTransitionProgress; + actionBar.setAlpha(1.0f - fullscreenProgress); + actionBar.setTranslationY(-ActionBar.getCurrentActionBarHeight() * fullscreenProgress); + fullscreenButtons.setAlpha(fullscreenProgress); + if (fullscreen) { + actionBar.setVisibility(View.GONE); + } + swipeContainer.setSwipeOffsetAnimationDisallowed(false); + swipeContainer.setTranslationX(lerp(fromTranslationX, toTranslationX, fullscreenTransitionProgress)); + botButtons.setTranslationX(0); + windowView.invalidate(); + webViewContainer.setViewPortHeightOffset(0); + webViewContainer.invalidateViewPortHeight(true, true); + updateDownloadBulletinArrow(); + } + }); + fullscreenAnimator.setDuration(280); + fullscreenAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + fullscreenAnimator.start(); + } else { + fullscreenInProgress = false; + fullscreenProgress = fullscreen ? 1.0f : 0.0f; + fullscreenTransitionProgress = 0.0f; + updateFullscreenLayout(); + updateWindowFlags(); + actionBar.setVisibility(fullscreen ? View.GONE : View.VISIBLE); + actionBar.setAlpha(1.0f - fullscreenProgress); + actionBar.setTranslationY(-ActionBar.getCurrentActionBarHeight() * fullscreenProgress); + botButtons.setTranslationX(0); + fullscreenButtons.setAlpha(fullscreenProgress); + webViewContainer.setViewPortHeightOffset(0); + webViewContainer.invalidateViewPortHeight(true, true); + updateDownloadBulletinArrow(); + } + } + + public void setNavigationBarColor(int color, boolean animated) { + int from = navBarColor; + int to = color; + + botButtons.setBackgroundColor(color, animated); + if (animated) { + ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(200); + animator.setInterpolator(CubicBezierInterpolator.DEFAULT); + animator.addUpdateListener(animation -> { + float progress = (float) animation.getAnimatedValue(); + navBarColor = ColorUtils.blendARGB(from, to, progress); + checkNavBarColor(); + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + float progress = 1f; + navBarColor = ColorUtils.blendARGB(from, to, progress); + checkNavBarColor(); + } + }); + animator.start(); + } else { + navBarColor = to; + checkNavBarColor(); + } + AndroidUtilities.setNavigationBarColor(getWindow(), navBarColor, false); + } + + public void setActionBarColor(int color, boolean isOverride, boolean animated) { int from = actionBarColor; - int navBarFrom = navBarColor; +// int navBarFrom = navBarColor; int to = color; int navBarTo = navigationBarColor(color); BotWebViewMenuContainer.ActionBarColorsAnimating actionBarColorsAnimating = new BotWebViewMenuContainer.ActionBarColorsAnimating(); - actionBarColorsAnimating.setFrom(overrideBackgroundColor ? actionBarColor : 0, resourcesProvider); - overrideBackgroundColor = isOverrideColor; + actionBarColorsAnimating.setFrom(overrideActionBarColor ? actionBarColor : 0, resourcesProvider); + overrideActionBarColor = isOverride; actionBarIsLight = ColorUtils.calculateLuminance(color) < 0.721f; - actionBarColorsAnimating.setTo(overrideBackgroundColor ? to : 0, resourcesProvider); + actionBarColorsAnimating.setTo(overrideActionBarColor ? to : 0, resourcesProvider); if (animated) { ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(200); @@ -1493,7 +2262,7 @@ public void setActionBarColor(int color, boolean isOverrideColor, boolean animat animator.addUpdateListener(animation -> { float progress = (float) animation.getAnimatedValue(); actionBarColor = ColorUtils.blendARGB(from, to, progress); - navBarColor = ColorUtils.blendARGB(navBarFrom, navBarTo, progress); +// navBarColor = ColorUtils.blendARGB(navBarFrom, navBarTo, progress); checkNavBarColor(); windowView.invalidate(); actionBar.setBackgroundColor(actionBarColor); @@ -1508,7 +2277,7 @@ public void setActionBarColor(int color, boolean isOverrideColor, boolean animat public void onAnimationEnd(Animator animation) { float progress = 1f; actionBarColor = ColorUtils.blendARGB(from, to, progress); - navBarColor = ColorUtils.blendARGB(navBarFrom, navBarTo, progress); +// navBarColor = ColorUtils.blendARGB(navBarFrom, navBarTo, progress); checkNavBarColor(); windowView.invalidate(); actionBar.setBackgroundColor(actionBarColor); @@ -1523,7 +2292,7 @@ public void onAnimationEnd(Animator animation) { } else { float progress = 1f; actionBarColor = to; - navBarColor = navBarTo; +// navBarColor = navBarTo; checkNavBarColor(); windowView.invalidate(); actionBar.setBackgroundColor(actionBarColor); @@ -1537,15 +2306,18 @@ public void onAnimationEnd(Animator animation) { } public void checkNavBarColor() { - if (!dismissed && LaunchActivity.instance != null) { + if (!superDismissed && LaunchActivity.instance != null) { LaunchActivity.instance.checkSystemBarColors(true, true, true, false); - //LaunchActivity.instance.setNavigationBarColor(fragment.getNavigationBarColor(), false); +// AndroidUtilities.setNavigationBarColor(getWindow(), navBarColor, false); + } + if (windowView != null) { + windowView.invalidate(); } } @Override public int getNavigationBarColor(int color) { - return navBarColor; + return ColorUtils.blendARGB(color, navBarColor, openedProgress); } public WindowView getWindowView() { @@ -1563,17 +2335,85 @@ public WindowView(Context context) { setWillNotDraw(false); } + @Override + protected boolean drawChild(@NonNull Canvas canvas, View child, long drawingTime) { + boolean restore = false; + if (child == swipeContainer && fullscreenInProgress && swipeContainerFromHeight > 0 && swipeContainerFromWidth > 0) { + canvas.save(); + canvas.clipRect( + child.getX(), child.getY(), + child.getX() + lerp(swipeContainerFromWidth, child.getWidth(), fullscreenTransitionProgress), + child.getY() + lerp(swipeContainerFromHeight, child.getHeight(), fullscreenTransitionProgress) + ); + restore = true; + } + boolean r = super.drawChild(canvas, child, drawingTime); + if (restore) { + canvas.restore(); + } + return r; + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + final BottomSheetTabs tabs = LaunchActivity.instance != null ? LaunchActivity.instance.getBottomSheetTabs() : null; + if (tabs != null && insets != null) { + final int bottomTabsHeight = (int) (tabs.getHeight(true) * (1.0f - fullscreenProgress)); + if (ev.getY() >= getHeight() - insets.bottom - bottomTabsHeight && ev.getY() <= getHeight() - insets.bottom && !AndroidUtilities.isTablet()) { + return tabs.touchEvent(ev.getAction(), ev.getX(), ev.getY() - (getHeight() - insets.bottom - bottomTabsHeight)); + } + } + return super.dispatchTouchEvent(ev); + } + private final Paint navbarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); @Override protected void dispatchDraw(Canvas canvas) { if (drawingFromOverlay) return; + if (passcodeView.getVisibility() != View.VISIBLE && fullscreenProgress < 1 && fullscreenProgress > 0) { + navbarPaint.setColor(Theme.multAlpha(navBarColor, openedProgress)); + if (navInsets.left > 0) { + canvas.drawRect(0, 0, navInsets.left, getHeight(), navbarPaint); + } + if (navInsets.top > 0) { + canvas.drawRect(0, 0, getWidth(), navInsets.top, navbarPaint); + } + if (navInsets.bottom > 0) { + canvas.drawRect(0, getHeight() - navInsets.bottom, getWidth(), getHeight(), navbarPaint); + } + if (navInsets.right > 0) { + canvas.drawRect(getWidth() - navInsets.right, 0, getWidth(), getHeight(), navbarPaint); + } + } + + boolean restore = false; + if (bottomTabsClip != null && !AndroidUtilities.isTablet()) { + canvas.save(); + canvas.translate(insets.left * (1.0f - fullscreenProgress), 0); + bottomTabsClip.clip(canvas, true, false, lerp(getWidth() - insets.left - insets.right, getWidth(), fullscreenProgress), getHeight(), 1.0f - fullscreenProgress); + canvas.translate(-insets.left * (1.0f - fullscreenProgress), 0); + restore = true; + } super.dispatchDraw(canvas); + if (restore) { + canvas.restore(); + } if (passcodeView.getVisibility() != View.VISIBLE) { - navbarPaint.setColor(navBarColor); - AndroidUtilities.rectTmp.set(0, getHeight() - getPaddingBottom(), getWidth(), getHeight() + AndroidUtilities.navigationBarHeight); - canvas.drawRect(AndroidUtilities.rectTmp, navbarPaint); + navbarPaint.setColor(Theme.multAlpha(navBarColor, openedProgress)); + if (navInsets.left > 0) { + canvas.drawRect(0, 0, navInsets.left * (1.0f - fullscreenProgress), getHeight(), navbarPaint); + } + if (navInsets.top > 0) { + canvas.drawRect(0, 0, getWidth(), navInsets.top * (1.0f - fullscreenProgress), navbarPaint); + } + if (navInsets.bottom > 0) { + canvas.drawRect(0, getHeight() - (navInsets.bottom * (botButtons != null && botButtons.getTotalHeight() > 0 ? 1.0f : 1.0f - fullscreenProgress)), getWidth(), getHeight(), navbarPaint); + } + if (navInsets.right > 0) { + canvas.drawRect(getWidth() - (navInsets.right * (1.0f - fullscreenProgress)), 0, getWidth(), getHeight(), navbarPaint); + } } } @@ -1584,19 +2424,34 @@ protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (passcodeView.getVisibility() != View.VISIBLE) { + canvas.save(); + if (bottomTabsClip != null) { + bottomTabsClip.clip(canvas, false, false, getWidth(), getHeight(), 1.0f - fullscreenProgress); + } + if (!overrideBackgroundColor) { - backgroundPaint.setColor(getColor(Theme.key_windowBackgroundWhite)); + final int color = getColor(Theme.key_windowBackgroundWhite); + backgroundPaint.setColor(color); + webViewContainer.setFlickerViewColor(color); + if (errorContainer != null) { + errorContainer.setDark(AndroidUtilities.computePerceivedBrightness(backgroundPaint.getColor()) <= .721f, false); + errorContainer.setBackgroundColor(backgroundPaint.getColor()); + } } AndroidUtilities.rectTmp.set(0, 0, getWidth(), getHeight()); canvas.drawRect(AndroidUtilities.rectTmp, dimPaint); + final int bottomTabsHeight = bottomTabs != null ? bottomTabs.getHeight(true) : 0; + actionBarPaint.setColor(actionBarColor); float radius = AndroidUtilities.dp(16) * (AndroidUtilities.isTablet() ? 1f : 1f - actionBarTransitionProgress); - AndroidUtilities.rectTmp.set(swipeContainer.getLeft(), AndroidUtilities.lerp(swipeContainer.getTranslationY(), 0, actionBarTransitionProgress), swipeContainer.getRight(), swipeContainer.getTranslationY() + AndroidUtilities.dp(24) + radius); + AndroidUtilities.rectTmp.set(lerp(swipeContainer.getLeft(), 0, fullscreenProgress), lerp(swipeContainer.getTranslationY(), 0, actionBarTransitionProgress), swipeContainer.getRight(), swipeContainer.getTranslationY() + AndroidUtilities.dp(24) + radius); canvas.drawRoundRect(AndroidUtilities.rectTmp, radius, radius, actionBarPaint); - AndroidUtilities.rectTmp.set(swipeContainer.getLeft(), swipeContainer.getTranslationY() + AndroidUtilities.dp(24), swipeContainer.getRight(), getHeight()); + AndroidUtilities.rectTmp.set(lerp(swipeContainer.getLeft(), 0, fullscreenProgress), swipeContainer.getTranslationY() + AndroidUtilities.dp(24), lerp(swipeContainer.getRight(), getWidth(), fullscreenProgress), getHeight() - bottomTabsHeight); canvas.drawRect(AndroidUtilities.rectTmp, backgroundPaint); + + canvas.restore(); } } @@ -1608,26 +2463,26 @@ public void draw(Canvas canvas) { float transitionProgress = AndroidUtilities.isTablet() ? 0 : actionBarTransitionProgress; linePaint.setColor(lineColor); - linePaint.setAlpha((int) (linePaint.getAlpha() * (1f - Math.min(0.5f, transitionProgress) / 0.5f))); + linePaint.setAlpha((int) (linePaint.getAlpha() * (1f - Math.min(0.5f, transitionProgress) / 0.5f) * (1.0f - fullscreenProgress))); canvas.save(); float scale = 1f - transitionProgress; - float y = AndroidUtilities.isTablet() ? AndroidUtilities.lerp(swipeContainer.getTranslationY() + AndroidUtilities.dp(12), AndroidUtilities.statusBarHeight / 2f, actionBarTransitionProgress) : - (AndroidUtilities.lerp(swipeContainer.getTranslationY(), AndroidUtilities.statusBarHeight + ActionBar.getCurrentActionBarHeight() / 2f, transitionProgress) + AndroidUtilities.dp(12)); + float y = AndroidUtilities.isTablet() ? lerp(swipeContainer.getTranslationY() + AndroidUtilities.dp(12), AndroidUtilities.statusBarHeight / 2f, actionBarTransitionProgress) : + (lerp(swipeContainer.getTranslationY(), AndroidUtilities.statusBarHeight + ActionBar.getCurrentActionBarHeight() / 2f, transitionProgress) + AndroidUtilities.dp(12)); canvas.scale(scale, scale, getWidth() / 2f, y); canvas.drawLine(getWidth() / 2f - AndroidUtilities.dp(16), y, getWidth() / 2f + AndroidUtilities.dp(16), y, linePaint); canvas.restore(); actionBarShadow.setAlpha((int) (actionBar.getAlpha() * 0xFF)); y = actionBar.getY() + actionBar.getTranslationY() + actionBar.getHeight(); - actionBarShadow.setBounds(0, (int)y, getWidth(), (int)(y + actionBarShadow.getIntrinsicHeight())); + actionBarShadow.setBounds(insets.left, (int) y, getWidth() - insets.right, (int) (y + actionBarShadow.getIntrinsicHeight())); actionBarShadow.draw(canvas); } @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN && (event.getY() <= AndroidUtilities.lerp(swipeContainer.getTranslationY() + AndroidUtilities.dp(24), 0, actionBarTransitionProgress) || + if (event.getAction() == MotionEvent.ACTION_DOWN && (event.getY() <= lerp(swipeContainer.getTranslationY() + AndroidUtilities.dp(24), 0, actionBarTransitionProgress) || event.getX() > swipeContainer.getRight() || event.getX() < swipeContainer.getLeft())) { dismiss(true, null); return true; @@ -1657,6 +2512,11 @@ public void setDrawingFromOverlay(boolean drawingFromOverlay) { if (this.drawingFromOverlay != drawingFromOverlay) { this.drawingFromOverlay = drawingFromOverlay; invalidate(); + updateWindowFlags(); + if (LaunchActivity.instance != null && fullscreen) { + LaunchActivity.instance.requestCustomNavigationBar(); + LaunchActivity.instance.setNavigationBarColor(navBarColor, false); + } } } @@ -1675,7 +2535,7 @@ public float drawInto(Canvas canvas, RectF finalRect, float progress, RectF clip clipPath.rewind(); float radius = dp(16) * (AndroidUtilities.isTablet() ? 1f : 1f - actionBarTransitionProgress); - final float r = AndroidUtilities.lerp(radius, dp(10), progress); + final float r = lerp(radius, dp(10), progress); clipPath.addRoundRect(clipRect, r, r, Path.Direction.CW); canvas.clipPath(clipPath); canvas.drawPaint(backgroundPaint); @@ -1694,51 +2554,58 @@ public float drawInto(Canvas canvas, RectF finalRect, float progress, RectF clip } - private BotWebViewAttachedSheet.MainButtonSettings mainButtonSettings; - public void setMainButton(BotWebViewAttachedSheet.MainButtonSettings s) { - mainButtonSettings = s; - - mainButton.setClickable(s.isActive); - mainButton.setText(s.text); - mainButton.setTextColor(s.textColor); - mainButton.setBackground(BotWebViewContainer.getMainButtonRippleDrawable(s.color)); - if (s.isVisible != mainButtonWasVisible) { - mainButtonWasVisible = s.isVisible; - mainButton.animate().cancel(); - if (s.isVisible) { - mainButton.setAlpha(0f); - mainButton.setVisibility(View.VISIBLE); - } - mainButton.animate().alpha(s.isVisible ? 1f : 0f).setDuration(150).setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (!s.isVisible) { - mainButton.setVisibility(View.GONE); - } - swipeContainer.requestLayout(); - } - }).start(); - } - radialProgressView.setProgressColor(s.textColor); - if (s.isProgressVisible != mainButtonProgressWasVisible) { - mainButtonProgressWasVisible = s.isProgressVisible; - radialProgressView.animate().cancel(); - if (s.isProgressVisible) { - radialProgressView.setAlpha(0f); - radialProgressView.setVisibility(View.VISIBLE); - } - radialProgressView.animate().alpha(s.isProgressVisible ? 1f : 0f) - .scaleX(s.isProgressVisible ? 1f : 0.1f) - .scaleY(s.isProgressVisible ? 1f : 0.1f) - .setDuration(250) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (!s.isProgressVisible) { - radialProgressView.setVisibility(View.GONE); - } - } - }).start(); + private boolean errorShown; + private String errorCode; + private ArticleViewer.ErrorContainer errorContainer; + public ArticleViewer.ErrorContainer createErrorContainer() { + if (errorContainer == null) { + swipeContainer.addView(errorContainer = new ArticleViewer.ErrorContainer(getContext()), LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + errorContainer.setTranslationY(-1); + errorContainer.buttonView.setOnClickListener(v -> { + BotWebViewContainer.MyWebView webView = webViewContainer.getWebView(); + if (webView != null) { + webView.reload(); + } + }); + errorContainer.setBackgroundColor(backgroundPaint.getColor()); + AndroidUtilities.updateViewVisibilityAnimated(errorContainer, errorShown, 1f, false); + } + return errorContainer; + } + + private static int shownLockedBots = 0; + public boolean attached = false; + public void setAttached(boolean b) { + if (attached == b) return; + if (attached = b) { + if (orientationLocked) { + shownLockedBots++; + } + } else { + if (orientationLocked) { + shownLockedBots--; + } + } + if (shownLockedBots > 0) { + AndroidUtilities.lockOrientation(getActivity()); + } else { + AndroidUtilities.unlockOrientation(getActivity()); + } + } + public void lockOrientation(boolean lock) { + if (orientationLocked == lock) return; + orientationLocked = lock; + if (attached) { + if (lock) { + shownLockedBots++; + } else { + shownLockedBots--; + } + } + if (shownLockedBots > 0) { + AndroidUtilities.lockOrientation(getActivity()); + } else { + AndroidUtilities.unlockOrientation(getActivity()); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/ChatAttachAlertBotWebViewLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/ChatAttachAlertBotWebViewLayout.java index f8201cd3404..f432c92df34 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/bots/ChatAttachAlertBotWebViewLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/ChatAttachAlertBotWebViewLayout.java @@ -9,9 +9,11 @@ import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.RenderNode; import android.os.Build; import android.os.Bundle; import android.text.TextPaint; +import android.util.Log; import android.view.GestureDetector; import android.view.Gravity; import android.view.MotionEvent; @@ -201,8 +203,9 @@ public boolean dispatchTouchEvent(MotionEvent ev) { } @Override - public void onWebViewCreated() { - swipeContainer.setWebView(webViewContainer.getWebView()); + public void onWebViewCreated(MyWebView webView) { + super.onWebViewCreated(webView); + swipeContainer.setWebView(webView); } }; swipeContainer = new WebViewSwipeContainer(context) { @@ -218,7 +221,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { lastSwipeTime = System.currentTimeMillis(); }); swipeContainer.setScrollEndListener(()-> webViewContainer.invalidateViewPortHeight(true)); - swipeContainer.setDelegate(() -> { + swipeContainer.setDelegate(byTap -> { if (!onCheckDismissByUser()) { swipeContainer.stickTo(0); } @@ -700,6 +703,36 @@ public void showJustAddedBulletin() { public static class WebViewSwipeContainer extends FrameLayout { public final static SimpleFloatPropertyCompat SWIPE_OFFSET_Y = new SimpleFloatPropertyCompat<>("swipeOffsetY", WebViewSwipeContainer::getSwipeOffsetY, WebViewSwipeContainer::setSwipeOffsetY); + private Object renderNode; + public Object getRenderNode() { + if (renderNode == null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + renderNode = new RenderNode("WebViewSwipeContainer"); + } + } + return renderNode; + } + + @Override + protected void dispatchDraw(@NonNull Canvas canvas) { + if (canvas.isHardwareAccelerated()) { + Canvas drawingCanvas = canvas; + if (renderNode != null) { + final RenderNode node = (RenderNode) renderNode; + node.setPosition(0, 0, getWidth(), getHeight()); + drawingCanvas = node.beginRecording(); + } + super.dispatchDraw(drawingCanvas); + if (renderNode != null) { + final RenderNode node = (RenderNode) renderNode; + node.endRecording(); + canvas.drawRenderNode(node); + } + } else { + super.dispatchDraw(canvas); + } + } + private final GestureDetectorCompat gestureDetector; public boolean isScrolling; private boolean isSwipeDisallowed; @@ -742,6 +775,10 @@ public void setFullSize(boolean fullsize) { } } + public boolean isFullSize() { + return fullsize; + } + private boolean allowFullSizeSwipe; public void setAllowFullSizeSwipe(boolean value) { allowFullSizeSwipe = value; @@ -788,7 +825,7 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float ve if (fullsize && allowFullSizeSwipe && drawnSwipeOffsetY == -offsetY + topActionBarOffsetY) { stickTo(-offsetY + topActionBarOffsetY); } else if (delegate != null) { - delegate.onDismiss(); + delegate.onDismiss(false); } } else { stickTo(0); @@ -910,6 +947,11 @@ public void setSwipeOffsetY(float swipeOffsetY) { invalidateTranslation(); } + public void setForceOffsetY(float offsetY) { + this.offsetY = offsetY; + invalidateTranslation(); + } + public void setOffsetY(float offsetY) { if (pendingSwipeOffsetY != Integer.MIN_VALUE) { pendingOffsetY = offsetY; @@ -977,7 +1019,7 @@ private void updateDrawn() { drawnSwipeOffsetY = swipeOffsetY; } - private void invalidateTranslation() { + public void invalidateTranslation() { setTranslationY(Math.max(topActionBarOffsetY, offsetY + swipeOffsetY)); AndroidUtilities.cancelRunOnUIThread(this::updateDrawn); AndroidUtilities.runOnUIThread(this::updateDrawn); @@ -991,6 +1033,11 @@ private void invalidateTranslation() { } } + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + } + public float getTopActionBarOffsetY() { return topActionBarOffsetY; } @@ -1033,6 +1080,7 @@ public boolean dispatchTouchEvent(MotionEvent ev) { rawEvent.recycle(); if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) { + final boolean wasScrolling = isScrolling; isSwipeDisallowed = false; isScrolling = false; @@ -1045,13 +1093,13 @@ public boolean dispatchTouchEvent(MotionEvent ev) { if (stickToEdges) { stickTo(-offsetY + topActionBarOffsetY); } - } else if (swipeOffsetY > -swipeStickyRange && swipeOffsetY <= swipeStickyRange && !fullsize) { + } else if (swipeOffsetY > -swipeStickyRange && swipeOffsetY <= swipeStickyRange) { if (stickToEdges) { stickTo(0); } } else { if (delegate != null) { - delegate.onDismiss(); + delegate.onDismiss(!wasScrolling); } } } @@ -1069,8 +1117,20 @@ public void stickTo(float offset) { stickTo(offset, null); } + public void cancelStickTo() { + if (offsetYAnimator != null) { + offsetYAnimator.cancel(); + } + if (scrollAnimator != null) { + scrollAnimator.cancel(); + } + } + public void stickTo(float offset, Runnable callback) { - if (fullsize) { + stickTo(offset, false, callback); + } + public void stickTo(float offset, boolean force, Runnable callback) { + if (fullsize && !force) { offset = -getOffsetY() + getTopActionBarOffsetY(); } if (swipeOffsetY == offset || scrollAnimator != null && scrollAnimator.getSpring().getFinalPosition() == offset) { @@ -1127,7 +1187,7 @@ public interface Delegate { /** * Called to dismiss parent layout */ - void onDismiss(); + void onDismiss(boolean byTap); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/SetupEmojiStatusSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/SetupEmojiStatusSheet.java new file mode 100644 index 00000000000..83d42083d8e --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/SetupEmojiStatusSheet.java @@ -0,0 +1,536 @@ +package org.telegram.ui.bots; + +import static android.graphics.PorterDuff.Mode.SRC_IN; +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.getDataColumn; +import static org.telegram.messenger.LocaleController.getCurrencyExpDivider; +import static org.telegram.messenger.LocaleController.getString; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.checkerframework.checker.units.qual.A; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.DocumentObject; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.ImageReceiver; +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; +import org.telegram.messenger.SvgHelper; +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.tgnet.tl.TL_bots; +import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.BottomSheet; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AlertsCreator; +import org.telegram.ui.Components.AnimatedEmojiDrawable; +import org.telegram.ui.Components.AnimatedFileDrawable; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.AttachableDrawable; +import org.telegram.ui.Components.AvatarDrawable; +import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.ChatAttachAlert; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.PhotoViewerCaptionEnterView; +import org.telegram.ui.Components.Premium.PremiumFeatureBottomSheet; +import org.telegram.ui.Components.Premium.PremiumPreviewBottomSheet; +import org.telegram.ui.Components.Text; +import org.telegram.ui.LaunchActivity; +import org.telegram.ui.PhotoViewer; +import org.telegram.ui.PremiumPreviewFragment; +import org.telegram.ui.Stories.recorder.ButtonWithCounterView; + +import java.util.ArrayList; + +public class SetupEmojiStatusSheet { + + public static void show(int currentAccount, TLRPC.User bot, long document_id, int duration, Utilities.Callback2 whenDone) { + if (whenDone == null) return; + + final TLRPC.Document emoji_document = AnimatedEmojiDrawable.findDocument(currentAccount, document_id); + if (emoji_document != null) { + show(currentAccount, bot, emoji_document, duration, err -> whenDone.run(err, emoji_document)); + return; + } + + AnimatedEmojiDrawable.getDocumentFetcher(currentAccount).fetchDocument(document_id, document -> { + AndroidUtilities.runOnUIThread(() -> { + show(currentAccount, bot, document, duration, err -> whenDone.run(err, document)); + }); + }); + } + + public static void show(int currentAccount, TLRPC.User bot, TLRPC.Document document, int duration, Utilities.Callback whenDone) { + if (whenDone == null) return; + if (document == null || document instanceof TLRPC.TL_documentEmpty) { + whenDone.run("SUGGESTED_EMOJI_INVALID"); + return; + } + + Context context = AndroidUtilities.findActivity(LaunchActivity.instance); + if (context == null) context = ApplicationLoader.applicationContext; + + final int now = ConnectionsManager.getInstance(currentAccount).getCurrentTime(); + final TLRPC.User currentUser = UserConfig.getInstance(currentAccount).getCurrentUser(); + + final boolean[] sentDone = new boolean[1]; + final boolean[] setting = new boolean[1]; + + final CharSequence message; + if (duration > 0) { + final int MINUTE = 60; + final int HOUR = 60 * MINUTE; + final int DAY = 24 * HOUR; + int total = duration; + final int d = total / DAY; total -= d * DAY; + final int h = total / HOUR; total -= h * HOUR; + final int m = Math.round((float) total / MINUTE); + StringBuilder durationString = new StringBuilder(); + if (d > 0) { + if (durationString.length() > 0) durationString.append(" "); + durationString.append(LocaleController.formatPluralString("BotEmojiStatusSetRequestForDay", d)); + } + if (h > 0) { + if (durationString.length() > 0) durationString.append(" "); + durationString.append(LocaleController.formatPluralString("BotEmojiStatusSetRequestForHour", h)); + } + if (m > 0) { + if (durationString.length() > 0) durationString.append(" "); + durationString.append(LocaleController.formatPluralString("BotEmojiStatusSetRequestForMinute", m)); + } + message = AndroidUtilities.replaceTags(LocaleController.formatString(R.string.BotEmojiStatusSetRequestFor, UserObject.getUserName(bot), durationString)); + } else { + message = AndroidUtilities.replaceTags(LocaleController.formatString(R.string.BotEmojiStatusSetRequest, UserObject.getUserName(bot))); + } + + AlertDialog dialog = new AlertDialog.Builder(context, null) + .setTopImage(new UserEmojiStatusDrawable(currentUser, document), Theme.getColor(Theme.key_dialogTopBackground)) + .setMessage(message) + .setPositiveButton(LocaleController.getString(R.string.BotEmojiStatusConfirm), (dialogInterface, i) -> { + if (!UserConfig.getInstance(currentAccount).isPremium()) { + new PremiumFeatureBottomSheet(new BaseFragment() { + @Override + public int getCurrentAccount() { + return currentAccount; + } + @Override + public Context getContext() { + return AndroidUtilities.findActivity(LaunchActivity.instance); + } + @Override + public Activity getParentActivity() { + Activity activity = AndroidUtilities.findActivity(ApplicationLoader.applicationContext); + if (activity == null) activity = LaunchActivity.instance; + return activity; + } + }, PremiumPreviewFragment.PREMIUM_FEATURE_EMOJI_STATUS, false).show(); + return; + } + setting[0] = true; + + TLRPC.TL_account_updateEmojiStatus req = new TLRPC.TL_account_updateEmojiStatus(); + if (duration > 0) { + TLRPC.TL_emojiStatusUntil status = new TLRPC.TL_emojiStatusUntil(); + status.until = ConnectionsManager.getInstance(currentAccount).getCurrentTime() + duration; + status.document_id = document.id; + req.emoji_status = status; + } else { + TLRPC.TL_emojiStatus status = new TLRPC.TL_emojiStatus(); + status.document_id = document.id; + req.emoji_status = status; + } + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (!(res instanceof TLRPC.TL_boolTrue)) { + if (!sentDone[0]) { + sentDone[0] = true; + whenDone.run("SERVER_ERROR"); + } + } else { + TLRPC.User user = UserConfig.getInstance(currentAccount).getCurrentUser(); + if (user != null) { + user.emoji_status = req.emoji_status; + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.userEmojiStatusUpdated, user); + MessagesController.getInstance(currentAccount).updateEmojiStatusUntilUpdate(user.id, user.emoji_status); + } + if (!sentDone[0]) { + sentDone[0] = true; + whenDone.run(null); + } + } + })); + }) + .setNegativeButton(LocaleController.getString(R.string.Cancel), null) + .create(); + dialog.show(); + dialog.setOnDismissListener(d -> { + if (!setting[0] && !sentDone[0]) { + sentDone[0] = true; + whenDone.run("USER_DECLINED"); + } + }); + } + + public static void askPermission(int currentAccount, long botId, Utilities.Callback2 whenDone) { + final TLRPC.User bot = MessagesController.getInstance(currentAccount).getUser(botId); + final TLRPC.UserFull botFull = MessagesController.getInstance(currentAccount).getUserFull(botId); + if (botFull == null) { + MessagesController.getInstance(currentAccount).loadFullUser(bot, 0, true, (userFull2) -> { + AndroidUtilities.runOnUIThread(() -> { + if (userFull2 == null) { + whenDone.run(false, "cancelled"); + return; + } + askPermission(currentAccount, bot, userFull2, whenDone); + }); + }); + } else { + askPermission(currentAccount, bot, botFull, whenDone); + } + } + + public static void askPermission(int currentAccount, TLRPC.User bot, TLRPC.UserFull botFull, Utilities.Callback2 whenDone) { + if (whenDone == null) return; + + if (botFull.bot_can_manage_emoji_status) { + whenDone.run(false, "allowed"); + return; + } + + Context context = AndroidUtilities.findActivity(LaunchActivity.instance); + if (context == null) context = ApplicationLoader.applicationContext; + final Context finalContext = context; + + final TLRPC.User currentUser = UserConfig.getInstance(currentAccount).getCurrentUser(); + + boolean[] sentDone = new boolean[1]; + boolean[] setting = new boolean[1]; + AlertDialog dialog = new AlertDialog.Builder(context, null) + .setTopImage(new UserEmojiStatusDrawable(currentUser), Theme.getColor(Theme.key_dialogTopBackground)) + .setMessage(AndroidUtilities.replaceTags(LocaleController.formatString(R.string.BotEmojiStatusPermissionRequest, UserObject.getUserName(bot), UserObject.getUserName(bot)))) + .setPositiveButton(LocaleController.getString(R.string.BotEmojiStatusPermissionAllow), (dialogInterface, i) -> { + if (!UserConfig.getInstance(currentAccount).isPremium()) { + new PremiumFeatureBottomSheet(new BaseFragment() { + @Override + public int getCurrentAccount() { + return currentAccount; + } + @Override + public Context getContext() { + return AndroidUtilities.findActivity(LaunchActivity.instance); + } + @Override + public Activity getParentActivity() { + Activity activity = AndroidUtilities.findActivity(ApplicationLoader.applicationContext); + if (activity == null) activity = LaunchActivity.instance; + return activity; + } + }, PremiumPreviewFragment.PREMIUM_FEATURE_EMOJI_STATUS, false).show(); + if (!setting[0] && !sentDone[0]) { + sentDone[0] = true; + whenDone.run(true, "cancelled"); + } + return; + } + setting[0] = true; + saveAccessRequested(finalContext, currentAccount, bot.id); + + TL_bots.toggleUserEmojiStatusPermission req = new TL_bots.toggleUserEmojiStatusPermission(); + req.bot = MessagesController.getInstance(currentAccount).getInputUser(bot); + req.enabled = true; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (!(res instanceof TLRPC.TL_boolTrue)) { + if (!sentDone[0]) { + sentDone[0] = true; + whenDone.run(true, "cancelled"); + } + } else { + botFull.bot_can_manage_emoji_status = true; + if (!sentDone[0]) { + sentDone[0] = true; + whenDone.run(true, "allowed"); + } + } + })); + }) + .setNegativeButton(LocaleController.getString(R.string.BotEmojiStatusPermissionDecline), null) + .create(); + dialog.show(); + dialog.setOnDismissListener(d -> { + if (!setting[0] && !sentDone[0]) { + sentDone[0] = true; + saveAccessRequested(finalContext, currentAccount, bot.id); + whenDone.run(true, "cancelled"); + } + }); + } + + public static class UserEmojiStatusDrawable extends Drawable implements AttachableDrawable, NotificationCenter.NotificationCenterDelegate { + + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint backgroundPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG); + private final ImageReceiver userImageReceiver = new ImageReceiver(); + private final ImageReceiver statusImageReceiver = new ImageReceiver(); + private int currentStatus = 1; + private final AnimatedEmojiDrawable[] emojis = new AnimatedEmojiDrawable[2]; + private final Text text; + private final RectF rect = new RectF(); + private final boolean highlight; + private final AnimatedFloat animatedSwap = new AnimatedFloat(this::invalidateSelf, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + + public UserEmojiStatusDrawable(TLRPC.User user) { + this.highlight = false; + + backgroundPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + backgroundPaint2.setColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + backgroundPaint2.setShadowLayer(dp(2.33f), 0, dp(2), Theme.multAlpha(0xFF000000, .18f)); + + AvatarDrawable avatarDrawable = new AvatarDrawable(); + avatarDrawable.setInfo(user); + userImageReceiver.setForUserOrChat(user, avatarDrawable); + userImageReceiver.setRoundRadius(dp(16)); + +// final TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(emojiStatus.thumbs, 120); +// final SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(emojiStatus.thumbs, Theme.key_windowBackgroundGray, 0.35f); +// statusImageReceiver.setImage( +// ImageLocation.getForDocument(emojiStatus), "120_120", +// ImageLocation.getForDocument(photoSize, emojiStatus), "120_120", +// svgThumb, 0, null, null, 0 +// ); + setRandomStatus(); + + text = new Text(UserObject.getUserName(user), 14); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.groupStickersDidLoad) { + if (waitingForStatuses && attached) { + waitingForStatuses = false; + setRandomStatus(); + } + } + } + + private boolean waitingForStatuses; + public void setRandomStatus() { + final TLRPC.TL_messages_stickerSet defaultSet = MediaDataController.getInstance(UserConfig.selectedAccount).getStickerSet(new TLRPC.TL_inputStickerSetEmojiDefaultStatuses(), false); + if (defaultSet == null || defaultSet.documents.isEmpty()) { + waitingForStatuses = true; + return; + } + final int randomIndex = (int) Math.floor(Math.random() * defaultSet.documents.size()); + final TLRPC.Document status = defaultSet.documents.get(randomIndex); + + currentStatus = 1 - currentStatus; + if (emojis[currentStatus] != null) { + emojis[currentStatus].removeView(view); + } + emojis[currentStatus] = AnimatedEmojiDrawable.make(UserConfig.selectedAccount, AnimatedEmojiDrawable.CACHE_TYPE_ALERT_EMOJI_STATUS, status); + emojis[currentStatus].setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_featuredStickers_addButton), SRC_IN)); + if (attached && emojis[currentStatus] != null) { + emojis[currentStatus].addView(view); + } + + AndroidUtilities.runOnUIThread(() -> { + if (!attached) return; + setRandomStatus(); + }, 2500); + } + + public UserEmojiStatusDrawable(TLRPC.User user, TLRPC.Document emojiStatus) { + this.highlight = true; + + backgroundPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + backgroundPaint2.setColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + backgroundPaint2.setShadowLayer(dp(2.33f), 0, dp(2), Theme.multAlpha(0xFF000000, .18f)); + + AvatarDrawable avatarDrawable = new AvatarDrawable(); + avatarDrawable.setInfo(user); + userImageReceiver.setForUserOrChat(user, avatarDrawable); + userImageReceiver.setRoundRadius(dp(16)); + + final TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(emojiStatus.thumbs, 120); + final SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(emojiStatus.thumbs, Theme.key_windowBackgroundGray, 0.35f); + statusImageReceiver.setImage( + ImageLocation.getForDocument(emojiStatus), "120_120", + ImageLocation.getForDocument(photoSize, emojiStatus), "120_120", + svgThumb, 0, null, null, 0 + ); + + text = new Text(UserObject.getUserName(user), 14); + } + + private boolean attached; + private View view; + + @Override + public void onAttachedToWindow(ImageReceiver parent) { + attached = true; + userImageReceiver.onAttachedToWindow(); + statusImageReceiver.onAttachedToWindow(); + NotificationCenter.getInstance(UserConfig.selectedAccount).addObserver(this, NotificationCenter.recentEmojiStatusesUpdate); + if (emojis[0] != null) { + emojis[0].addView(view); + } + if (emojis[1] != null) { + emojis[1].addView(view); + } + } + + @Override + public void onDetachedFromWindow(ImageReceiver parent) { + attached = false; + userImageReceiver.onDetachedFromWindow(); + statusImageReceiver.onDetachedFromWindow(); + NotificationCenter.getInstance(UserConfig.selectedAccount).removeObserver(this, NotificationCenter.recentEmojiStatusesUpdate); + if (emojis[0] != null) { + emojis[0].removeView(view); + } + if (emojis[1] != null) { + emojis[1].removeView(view); + } + } + + @Override + public void setParent(View view) { + this.view = view; + statusImageReceiver.setParentView(view); + userImageReceiver.setParentView(view); + } + + @Override + public void draw(@NonNull Canvas canvas) { + final Rect bounds = getBounds(); + + final float width = dp(32 + 6 + (highlight ? 48 : 28) + 6.66f) + text.getCurrentWidth(); + final float height = dp(32); + + rect.set( + bounds.centerX() - width / 2f, + bounds.centerY() - height / 2f, + bounds.centerX() + width / 2f, + bounds.centerY() + height / 2f + ); + canvas.drawRoundRect( + rect, + height / 2f, height / 2f, + backgroundPaint + ); + userImageReceiver.setImageCoords(rect.left, rect.top, dp(32), dp(32)); + userImageReceiver.draw(canvas); + text.draw(canvas, rect.left + dp(32 + 4), rect.centerY(), Theme.getColor(Theme.key_windowBackgroundWhiteBlackText), 1.0f); + + if (highlight) { + final float cx = rect.right - dp(6.66f + 16), r = dp(24); + canvas.drawCircle(cx, rect.centerY(), r, backgroundPaint2); + + statusImageReceiver.setImageCoords(cx - dp(16), rect.centerY() - dp(16), dp(32), dp(32)); + statusImageReceiver.draw(canvas); + } else { + float index = animatedSwap.set(currentStatus); + canvas.save(); + canvas.translate((int) (rect.right - dp(6.66f + 24)), (int) (rect.centerY() - dp(12))); + if (index < 1) { + AnimatedEmojiDrawable emoji = emojis[0]; + if (emoji != null) { + canvas.save(); + canvas.translate(0, (currentStatus == 0 ? -1 : +1) * dp(9) * index); + final float s = .6f + .4f * (1.0f - index); + canvas.scale(s, s, dp(12), dp(12)); + emoji.setBounds(0, 0, dp(24), dp(24)); + emoji.setAlpha((int) (0xFF * (1.0f - index))); + emoji.draw(canvas); + canvas.restore(); + } + } + if (index > 0) { + AnimatedEmojiDrawable emoji = emojis[1]; + if (emoji != null) { + canvas.save(); + canvas.translate(0, (currentStatus == 1 ? -1 : +1) * dp(9) * (1.0f - index)); + final float s = .6f + .4f * index; + canvas.scale(s, s, dp(12), dp(12)); + emoji.setBounds(0, 0, dp(24), dp(24)); + emoji.setAlpha((int) (0xFF * index)); + emoji.draw(canvas); + canvas.restore(); + } + } + canvas.restore(); + } + } + + @Override + public void setAlpha(int alpha) { + + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSPARENT; + } + } + + public static final String PREF = "botemojistatus_"; + public static boolean getAccessRequested(Context context, int currentAccount, long botId) { + if (context == null) return false; + SharedPreferences prefs = context.getSharedPreferences(PREF + currentAccount, Activity.MODE_PRIVATE); + return prefs.getBoolean("requested_" + botId, false); + } + + public static void saveAccessRequested(Context context, int currentAccount, long botId) { + if (context == null) return; + SharedPreferences prefs = context.getSharedPreferences(PREF + currentAccount, Activity.MODE_PRIVATE); + prefs.edit().putBoolean("requested_" + botId, true).apply(); + } + + public static void clear() { + Context context = ApplicationLoader.applicationContext; + if (context == null) return; + for (int i = 0; i < UserConfig.MAX_ACCOUNT_COUNT; ++i) { + final SharedPreferences prefs = context.getSharedPreferences(PREF + i, Activity.MODE_PRIVATE); + prefs.edit().clear().apply(); + } + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/WebViewRequestProps.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/WebViewRequestProps.java index 11edd8c7122..377cab3b5c7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/bots/WebViewRequestProps.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/WebViewRequestProps.java @@ -26,26 +26,28 @@ public class WebViewRequestProps { public TLRPC.User botUser; public int flags; public boolean compact; + public boolean fullscreen; public TLObject response; public long responseTime; public static WebViewRequestProps of( - int currentAccount, - long peerId, - long botId, - String buttonText, - String buttonUrl, - @BotWebViewAttachedSheet.WebViewType int type, - int replyToMsgId, - boolean silent, - TLRPC.BotApp app, - boolean allowWrite, - String startParam, - TLRPC.User botUser, - int flags, - boolean compact + int currentAccount, + long peerId, + long botId, + String buttonText, + String buttonUrl, + @BotWebViewAttachedSheet.WebViewType int type, + int replyToMsgId, + boolean silent, + TLRPC.BotApp app, + boolean allowWrite, + String startParam, + TLRPC.User botUser, + int flags, + boolean compact, + boolean fullscreen ) { WebViewRequestProps p = new WebViewRequestProps(); p.currentAccount = currentAccount; @@ -62,10 +64,12 @@ public static WebViewRequestProps of( p.botUser = botUser; p.flags = flags; p.compact = compact; - if (!compact && !TextUtils.isEmpty(buttonUrl)) { + p.fullscreen = fullscreen; + if (!compact && !fullscreen && !TextUtils.isEmpty(buttonUrl)) { try { Uri uri = Uri.parse(buttonUrl); p.compact = TextUtils.equals(uri.getQueryParameter("mode"), "compact"); + p.fullscreen = TextUtils.equals(uri.getQueryParameter("mode"), "fullscreen"); } catch (Exception e) { FileLog.e(e); } @@ -84,18 +88,18 @@ public boolean equals(@Nullable Object obj) { return false; final WebViewRequestProps p = (WebViewRequestProps) obj; return ( - currentAccount == p.currentAccount && - peerId == p.peerId && - botId == p.botId && - TextUtils.equals(buttonUrl, p.buttonUrl) && - type == p.type && - replyToMsgId == p.replyToMsgId && - silent == p.silent && - (app == null ? 0 : app.id) == (p.app == null ? 0 : p.app.id) && - allowWrite == p.allowWrite && - TextUtils.equals(startParam, p.startParam) && - (botUser == null ? 0 : botUser.id) == (p.botUser == null ? 0 : p.botUser.id) && - flags == p.flags + currentAccount == p.currentAccount && + peerId == p.peerId && + botId == p.botId && + TextUtils.equals(buttonUrl, p.buttonUrl) && + type == p.type && + replyToMsgId == p.replyToMsgId && + silent == p.silent && + (app == null ? 0 : app.id) == (p.app == null ? 0 : p.app.id) && + allowWrite == p.allowWrite && + TextUtils.equals(startParam, p.startParam) && + (botUser == null ? 0 : botUser.id) == (p.botUser == null ? 0 : p.botUser.id) && + flags == p.flags ); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/web/BotWebViewContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/web/BotWebViewContainer.java index 2ac51214865..b73d5c053be 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/web/BotWebViewContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/web/BotWebViewContainer.java @@ -1,5 +1,6 @@ package org.telegram.ui.web; +import static org.telegram.messenger.AndroidUtilities.dp; import static org.telegram.messenger.AndroidUtilities.readRes; import static org.telegram.messenger.LocaleController.formatString; import static org.telegram.messenger.LocaleController.getString; @@ -23,8 +24,10 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.net.Uri; import android.net.http.SslError; @@ -33,11 +36,13 @@ import android.os.Message; import android.text.SpannableStringBuilder; import android.text.TextUtils; +import android.util.Log; import android.util.Pair; import android.util.TypedValue; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; +import android.webkit.ConsoleMessage; import android.webkit.CookieManager; import android.webkit.DownloadListener; import android.webkit.GeolocationPermissions; @@ -57,6 +62,7 @@ import android.widget.FrameLayout; import android.widget.TextView; +import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; @@ -101,6 +107,7 @@ import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.ActionBar.BottomSheetTabs; +import org.telegram.ui.ActionBar.INavigationLayout; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ArticleViewer; import org.telegram.ui.CameraScanActivity; @@ -115,13 +122,19 @@ import org.telegram.ui.Components.voip.CellFlickerDrawable; import org.telegram.ui.LaunchActivity; import org.telegram.ui.PremiumPreviewFragment; +import org.telegram.ui.ProfileActivity; import org.telegram.ui.Stories.recorder.StoryEntry; import org.telegram.ui.Stories.recorder.StoryRecorder; import org.telegram.ui.WrappedResourceProvider; import org.telegram.ui.bots.BotBiometry; import org.telegram.ui.bots.BotBiometrySettings; +import org.telegram.ui.bots.BotDownloads; +import org.telegram.ui.bots.BotLocation; +import org.telegram.ui.bots.BotSensors; +import org.telegram.ui.bots.BotShareSheet; import org.telegram.ui.bots.BotWebViewSheet; import org.telegram.ui.bots.ChatAttachAlertBotWebViewLayout; +import org.telegram.ui.bots.SetupEmojiStatusSheet; import org.telegram.ui.bots.WebViewRequestProps; import java.io.File; @@ -130,7 +143,6 @@ import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.IDN; -import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; @@ -156,6 +168,7 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific private boolean webViewNotAvailable; private final CellFlickerDrawable flickerDrawable = new CellFlickerDrawable(); + private SvgHelper.SvgDrawable flickerViewDrawable; private BackupImageView flickerView; private boolean isFlickeringCenter; @@ -174,7 +187,7 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific private String lastSecondaryButtonPosition = ""; private String secondaryButtonData; - private int currentAccount; + private int currentAccount = UserConfig.selectedAccount; private boolean isPageLoaded; private boolean lastExpanded; private boolean isRequestingPageOpen; @@ -204,8 +217,12 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific private String lastQrText; private BotBiometry biometry; + private BotLocation location; + private BotDownloads downloads; public final boolean bot; + private BotSensors sensors; + public void showLinkCopiedBulletin() { BulletinFactory.of(this, resourcesProvider).createCopyLinkBulletin().show(true); } @@ -258,7 +275,7 @@ protected void onDraw(Canvas canvas) { } } }; - flickerView.setColorFilter(new PorterDuffColorFilter(getColor(Theme.key_dialogSearchHint), PorterDuff.Mode.SRC_IN)); + flickerView.setColorFilter(new PorterDuffColorFilter(flickerViewColor = getColor(Theme.key_bot_loadingIcon), PorterDuff.Mode.SRC_IN)); flickerView.getImageReceiver().setAspectFit(true); addView(flickerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP)); @@ -268,7 +285,7 @@ protected void onDraw(Canvas canvas) { webViewNotAvailableText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); webViewNotAvailableText.setGravity(Gravity.CENTER); webViewNotAvailableText.setVisibility(GONE); - int padding = AndroidUtilities.dp(16); + int padding = dp(16); webViewNotAvailableText.setPadding(padding, padding, padding, padding); addView(webViewNotAvailableText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); @@ -278,6 +295,27 @@ public void setViewPortByMeasureSuppressed(boolean viewPortByMeasureSuppressed) isViewPortByMeasureSuppressed = viewPortByMeasureSuppressed; } + private int flickerViewColor; + private boolean flickerViewColorOverriden; + public void setFlickerViewColor(int bgColor) { + final boolean light = AndroidUtilities.computePerceivedBrightness(bgColor) > .7f; + final int color; + if (light) { + color = Theme.adaptHSV(bgColor, 0, -.15f); + } else { + color = Theme.adaptHSV(bgColor, +.025f, +.15f); + } + if (flickerViewColor == color) return; + flickerView.setColorFilter(new PorterDuffColorFilter(flickerViewColor = color, PorterDuff.Mode.SRC_IN)); + if (flickerViewDrawable != null) { + flickerViewDrawable.setColor(flickerViewColor); + flickerViewDrawable.setupGradient(Theme.key_bot_loadingIcon, resourcesProvider, 1.0f, false); + } + flickerViewColorOverriden = true; + flickerView.invalidate(); + invalidate(); + } + public void checkCreateWebView() { if (webView == null && !webViewNotAvailable) { try { @@ -295,8 +333,12 @@ public void checkCreateWebView() { } } - public void replaceWebView(MyWebView webView, Object proxy) { + public void replaceWebView(int currentAccount, MyWebView webView, Object proxy) { + this.currentAccount = currentAccount; setupWebView(webView, proxy); + if (bot) { + notifyEvent("visibility_changed", obj("is_visible", true)); + } } private void setupWebView(MyWebView replaceWith) { @@ -351,6 +393,9 @@ private void setupWebView(MyWebView replaceWith, Object proxy) { } else { webView.setBackgroundColor(getColor(Theme.key_windowBackgroundWhite)); } + if (!MessagesController.getInstance(currentAccount).disableBotFullscreenBlur) { + webView.setLayerType(LAYER_TYPE_HARDWARE, null); + } webView.setContainers(this, webViewScrollListener); webView.setCloseListener(onCloseListener); WebSettings settings = webView.getSettings(); @@ -436,15 +481,15 @@ private void setupWebView(MyWebView replaceWith, Object proxy) { } } - onWebViewCreated(); + onWebViewCreated(webView); firstWebView = false; } private void onOpenUri(Uri uri) { - onOpenUri(uri, null, !bot, false); + onOpenUri(uri, null, !bot, false, false); } - private void onOpenUri(Uri uri, String browser, boolean tryInstantView, boolean suppressPopup) { + private void onOpenUri(Uri uri, String browser, boolean tryInstantView, boolean suppressPopup, boolean forceRequest) { if (isRequestingPageOpen || System.currentTimeMillis() - lastClickMs > 10_000 && suppressPopup) { return; } @@ -457,7 +502,7 @@ private void onOpenUri(Uri uri, String browser, boolean tryInstantView, boolean setKeyboardFocusable(false); } - Browser.openUrl(getContext(), uri, true, tryInstantView, false, null, browser, false, true); + Browser.openUrl(getContext(), uri, true, tryInstantView, false, null, browser, false, true, forceRequest); } private boolean keyboardFocusable; @@ -635,11 +680,15 @@ private boolean checkPermissions(String[] permissions) { } public void restoreButtonData() { - if (buttonData != null) { - onEventReceived("web_app_setup_main_button", buttonData); - } - if (secondaryButtonData != null) { - onEventReceived("web_app_setup_secondary_button", secondaryButtonData); + try { + if (buttonData != null) { + onEventReceived(botWebViewProxy, "web_app_setup_main_button", buttonData); + } + if (secondaryButtonData != null) { + onEventReceived(botWebViewProxy, "web_app_setup_secondary_button", secondaryButtonData); + } + } catch (Exception e) { + FileLog.e(e); } } @@ -721,6 +770,21 @@ public void invalidateViewPortHeight(boolean isStable) { private int lastViewportHeightReported; private boolean lastViewportStateStable; private boolean lastViewportIsExpanded; + private float viewPortHeightOffset; + + public int getMinHeight() { + if (getParent() instanceof ChatAttachAlertBotWebViewLayout.WebViewSwipeContainer) { + ChatAttachAlertBotWebViewLayout.WebViewSwipeContainer swipeContainer = (ChatAttachAlertBotWebViewLayout.WebViewSwipeContainer) getParent(); +// if (swipeContainer.isFullSize()) { +// return (int) (swipeContainer.getMeasuredHeight() - swipeContainer.getOffsetY() - swipeContainer.getTopActionBarOffsetY() + viewPortHeightOffset); +// } + } + return 0; + } + + public void setViewPortHeightOffset(float viewPortHeightOffset) { + this.viewPortHeightOffset = viewPortHeightOffset; + } public void invalidateViewPortHeight(boolean isStable, boolean force) { invalidate(); @@ -735,7 +799,7 @@ public void invalidateViewPortHeight(boolean isStable, boolean force) { lastExpanded = swipeContainer.getSwipeOffsetY() == -swipeContainer.getOffsetY() + swipeContainer.getTopActionBarOffsetY(); } - int viewPortHeight = (int) (swipeContainer.getMeasuredHeight() - swipeContainer.getOffsetY() - swipeContainer.getSwipeOffsetY() + swipeContainer.getTopActionBarOffsetY()); + final int viewPortHeight = Math.max(getMinHeight(), (int) (swipeContainer.getMeasuredHeight() - swipeContainer.getOffsetY() - swipeContainer.getSwipeOffsetY() + swipeContainer.getTopActionBarOffsetY() + viewPortHeightOffset)); if ( force || viewPortHeight != lastViewportHeightReported || @@ -746,13 +810,11 @@ public void invalidateViewPortHeight(boolean isStable, boolean force) { lastViewportStateStable = isStable; lastViewportIsExpanded = lastExpanded; - try { - JSONObject data = new JSONObject(); - data.put("height", viewPortHeight / AndroidUtilities.density); - data.put("is_state_stable", isStable); - data.put("is_expanded", lastExpanded); - notifyEvent("viewport_changed", data); - } catch (JSONException ignore) {} + StringBuilder sb = new StringBuilder(); + sb.append("{height:").append(viewPortHeight / AndroidUtilities.density).append(","); + sb.append("is_state_stable:").append(isStable).append(","); + sb.append("is_expanded:").append(lastExpanded).append("}"); + notifyEvent_fast("viewport_changed", sb.toString()); } } } @@ -770,9 +832,11 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { canvas.restore(); } - AndroidUtilities.rectTmp.set(0, 0, getWidth(), getHeight()); - flickerDrawable.draw(canvas, AndroidUtilities.rectTmp, 0, this); - invalidate(); + if (!isFlickeringCenter) { + AndroidUtilities.rectTmp.set(0, 0, getWidth(), getHeight()); + flickerDrawable.draw(canvas, AndroidUtilities.rectTmp, 0, this); + invalidate(); + } return draw; } if (child == webViewNotAvailableText) { @@ -789,8 +853,18 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { return super.drawChild(canvas, child, drawingTime); } + private int forceHeight = -1; + public void setForceHeight(int height) { + if (this.forceHeight == height) return; + this.forceHeight = height; + requestLayout(); + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (forceHeight >= 0) { + heightMeasureSpec = MeasureSpec.makeMeasureSpec(forceHeight, MeasureSpec.EXACTLY); + } super.onMeasure(widthMeasureSpec, heightMeasureSpec); flickerDrawable.setParentWidth(BotWebViewContainer.this.getMeasuredWidth()); @@ -805,12 +879,13 @@ public MyWebView getWebView() { } public void loadFlickerAndSettingsItem(int currentAccount, long botId, ActionBarMenuSubItem settingsItem) { - TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(botId); + final TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(botId); + final TLRPC.UserFull userFull = MessagesController.getInstance(currentAccount).getUserFull(botId); String username = UserObject.getPublicUsername(user); if (username != null && Objects.equals(username, DURGER_KING_USERNAME)) { flickerView.setVisibility(VISIBLE); flickerView.setAlpha(1f); - flickerView.setImageDrawable(SvgHelper.getDrawable(R.raw.durgerking_placeholder, getColor(Theme.key_windowBackgroundGray))); + flickerView.setImage(null, null, SvgHelper.getDrawable(R.raw.durgerking_placeholder, getColor(Theme.key_windowBackgroundGray))); setupFlickerParams(false); return; } @@ -836,27 +911,36 @@ public void loadFlickerAndSettingsItem(int currentAccount, long botId, ActionBar flickerView.setImage(ImageLocation.getForDocument(botIcon.icon), null, (Drawable) null, cachedBot); setupFlickerParams(center); } + } else if (userFull != null && userFull.bot_info != null && userFull.bot_info.app_settings != null && userFull.bot_info.app_settings.placeholder_svg_path != null) { + flickerView.setVisibility(VISIBLE); + flickerView.setAlpha(1f); + flickerViewDrawable = SvgHelper.getDrawableByPath(userFull.bot_info.app_settings.placeholder_svg_path, 512, 512); + if (flickerViewDrawable != null) { + flickerViewDrawable.setColor(flickerViewColor); + flickerViewDrawable.setupGradient(Theme.key_bot_loadingIcon, resourcesProvider, 1.0f, false); + } + flickerView.setImage(null, null, flickerViewDrawable); + setupFlickerParams(true); } else { - TLRPC.TL_messages_getAttachMenuBot req = new TLRPC.TL_messages_getAttachMenuBot(); - req.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { - if (response instanceof TLRPC.TL_attachMenuBotsBot) { - TLRPC.TL_attachMenuBot bot = ((TLRPC.TL_attachMenuBotsBot) response).bot; - - boolean center = false; - TLRPC.TL_attachMenuBotIcon botIcon = MediaDataController.getPlaceholderStaticAttachMenuBotIcon(bot); - if (botIcon == null) { - botIcon = MediaDataController.getStaticAttachMenuBotIcon(bot); - center = true; - } - if (botIcon != null) { - flickerView.setVisibility(VISIBLE); - flickerView.setAlpha(1f); - flickerView.setImage(ImageLocation.getForDocument(botIcon.icon), null, (Drawable) null, bot); - setupFlickerParams(center); - } - } - })); + Path path = new Path(); + final float c = 256, sz = 133.69f, hp = 31.29f / 2.0f; + AndroidUtilities.rectTmp.set(c - sz - hp, c - sz - hp, c - hp, c - hp); + path.addRoundRect(AndroidUtilities.rectTmp, 18, 18, Path.Direction.CW); + AndroidUtilities.rectTmp.set(c + hp, c - sz - hp, c + sz + hp, c - hp); + path.addRoundRect(AndroidUtilities.rectTmp, 18, 18, Path.Direction.CW); + AndroidUtilities.rectTmp.set(c - sz - hp, c + hp, c - hp, c + sz + hp); + path.addRoundRect(AndroidUtilities.rectTmp, 18, 18, Path.Direction.CW); + AndroidUtilities.rectTmp.set(c + hp, c + hp, c + sz + hp, c + sz + hp); + path.addRoundRect(AndroidUtilities.rectTmp, 18, 18, Path.Direction.CW); + flickerView.setVisibility(VISIBLE); + flickerView.setAlpha(1f); + flickerViewDrawable = SvgHelper.getDrawableByPath(path, 512, 512); + if (flickerViewDrawable != null) { + flickerViewDrawable.setColor(flickerViewColor); + flickerViewDrawable.setupGradient(Theme.key_bot_loadingIcon, resourcesProvider, 1.0f, false); + } + flickerView.setImage(null, null, flickerViewDrawable); + setupFlickerParams(true); } } @@ -865,7 +949,7 @@ private void setupFlickerParams(boolean center) { FrameLayout.LayoutParams params = (LayoutParams) flickerView.getLayoutParams(); params.gravity = center ? Gravity.CENTER : Gravity.TOP; if (center) { - params.width = params.height = AndroidUtilities.dp(64); + params.width = params.height = dp(100); } else { params.width = LayoutParams.MATCH_PARENT; params.height = LayoutParams.WRAP_CONTENT; @@ -892,6 +976,10 @@ public void reload() { webView.reload(); } updateKeyboardFocusable(); + + if (sensors != null) { + sensors.stopAll(); + } }); } @@ -948,6 +1036,9 @@ protected void onDetachedFromWindow() { public void preserveWebView() { d("preserveWebView"); preserving = true; + if (bot) { + notifyEvent("visibility_changed", obj("is_visible", false)); + } } public void destroyWebView() { @@ -958,9 +1049,18 @@ public void destroyWebView() { } if (!preserving) { webView.destroy(); + onWebViewDestroyed(webView); } isPageLoaded = false; updateKeyboardFocusable(); + + if (biometry != null) { + biometry = null; + } + if (location != null) { + location.unlisten(this.notifyLocationChecked); + location = null; + } } } @@ -995,7 +1095,14 @@ public void didReceivedNotification(int id, int account, Object... args) { if (webView != null) { webView.setBackgroundColor(getColor(Theme.key_windowBackgroundWhite)); } - flickerView.setColorFilter(new PorterDuffColorFilter(getColor(Theme.key_dialogSearchHint), PorterDuff.Mode.SRC_IN)); + if (!flickerViewColorOverriden) { + flickerView.setColorFilter(new PorterDuffColorFilter(flickerViewColor = getColor(Theme.key_bot_loadingIcon), PorterDuff.Mode.SRC_IN)); + if (flickerViewDrawable != null) { + flickerViewDrawable.setColor(flickerViewColor); + flickerViewDrawable.setupGradient(Theme.key_bot_loadingIcon, resourcesProvider, 1.0f, false); + } + flickerView.invalidate(); + } notifyThemeChanged(); } else if (id == NotificationCenter.onActivityResultReceived) { onActivityResult((int) args[0], (int) args[1], (Intent) args[2]); @@ -1013,6 +1120,16 @@ private void notifyEvent(String event, JSONObject eventData) { evaluateJs("window.Telegram.WebView.receiveEvent('" + event + "', " + eventData + ");", false); } + private void notifyEvent_fast(String event, String eventData) { + StringBuilder sb = new StringBuilder(); + sb.append("window.Telegram.WebView.receiveEvent('"); + sb.append(event); + sb.append("', "); + sb.append(eventData); + sb.append(");"); + evaluateJs(sb.toString(), false); + } + private static void notifyEvent(int currentAccount, MyWebView webView, String event, JSONObject eventData) { if (webView == null) return; NotificationCenter.getInstance(currentAccount).doOnIdle(() -> { @@ -1104,7 +1221,7 @@ private void onWebEventReceived(String type, String data) { } } - private void onEventReceived(String eventType, String eventData) { + private void onEventReceived(BotWebViewProxy proxy, String eventType, String eventData) { if (!bot) { return; } @@ -1517,7 +1634,7 @@ public void didReceivedNotification(int id, int account, Object... args) { String browser = jsonData.optString("try_browser"); if (MessagesController.getInstance(currentAccount).webAppAllowedProtocols != null && MessagesController.getInstance(currentAccount).webAppAllowedProtocols.contains(uri.getScheme())) { - onOpenUri(uri, browser, jsonData.optBoolean("try_instant_view"), true); + onOpenUri(uri, browser, jsonData.optBoolean("try_instant_view"), true, false); } } catch (Exception e) { FileLog.e(e); @@ -1528,10 +1645,11 @@ public void didReceivedNotification(int id, int account, Object... args) { try { JSONObject jsonData = new JSONObject(eventData); String pathFull = jsonData.optString("path_full"); + boolean force_request = jsonData.optBoolean("force_request", false); if (pathFull.startsWith("/")) { pathFull = pathFull.substring(1); } - onOpenUri(Uri.parse("https://t.me/" + pathFull), null, false, true); + onOpenUri(Uri.parse("https://t.me/" + pathFull), null, false, true, force_request); } catch (JSONException e) { FileLog.e(e); } @@ -1997,18 +2115,20 @@ public void didReceivedNotification(int id, int account, Object... args) { break; } case "web_app_biometry_open_settings": { - if (isRequestingPageOpen || System.currentTimeMillis() - lastClickMs > 10_000) { + if (isRequestingPageOpen || botUser == null || System.currentTimeMillis() - lastClickMs > 10_000) { return; } lastClickMs = 0; - BaseFragment lastFragment = LaunchActivity.getLastFragment(); - if (lastFragment == null) return; - BaseFragment.BottomSheetParams params = new BaseFragment.BottomSheetParams(); - params.transitionFromLeft = true; - params.allowNestedScroll = false; - lastFragment.showAsSheet(new BotBiometrySettings(), params); + BaseFragment lastFragment = LaunchActivity.getSafeLastFragment(); + if (lastFragment == null || lastFragment.getParentLayout() == null) return; + final INavigationLayout parentLayout = lastFragment.getParentLayout(); + lastFragment.presentFragment(ProfileActivity.of(botUser.id)); + AndroidUtilities.scrollToFragmentRow(parentLayout, "botPermissionBiometry"); + if (delegate != null) { + delegate.onCloseToTabs(); + } break; } @@ -2145,6 +2265,317 @@ public boolean isLightStatusBar() { break; } + case "web_app_request_fullscreen": { + final String err; + if ((err = delegate.onFullscreenRequested(true)) == null) { + notifyEvent("fullscreen_changed", obj("is_fullscreen", true)); + } else { + notifyEvent("fullscreen_failed", obj("error", err)); + } + break; + } + case "web_app_exit_fullscreen": { + final String err; + if ((err = delegate.onFullscreenRequested(false)) == null) { + notifyEvent("fullscreen_changed", obj("is_fullscreen", false)); + } else { + notifyEvent("fullscreen_failed", obj("error", err)); + } + break; + } + case "web_app_start_accelerometer": { + final BotSensors sensors = delegate.getBotSensors(); + long refresh_rate = 1000; + try { + refresh_rate = new JSONObject(eventData).getLong("refresh_rate"); + } catch (Exception e) {} + refresh_rate = Utilities.clamp(refresh_rate, 1000, 20); + if (sensors != null && sensors.startAccelerometer(refresh_rate)) { + notifyEvent("accelerometer_started", null); + } else { + notifyEvent("accelerometer_failed", obj("error", "UNSUPPORTED")); + } + break; + } + case "web_app_stop_accelerometer": { + final BotSensors sensors = delegate.getBotSensors(); + if (sensors != null && sensors.stopAccelerometer()) { + notifyEvent("accelerometer_stopped", null); + } else { + notifyEvent("accelerometer_failed", obj("error", "UNSUPPORTED")); + } + break; + } + case "web_app_start_gyroscope": { + final BotSensors sensors = delegate.getBotSensors(); + long refresh_rate = 1000; + try { + refresh_rate = new JSONObject(eventData).getLong("refresh_rate"); + } catch (Exception e) {} + refresh_rate = Utilities.clamp(refresh_rate, 1000, 20); + if (sensors != null && sensors.startGyroscope(refresh_rate)) { + notifyEvent("gyroscope_started", null); + } else { + notifyEvent("gyroscope_failed", obj("error", "UNSUPPORTED")); + } + break; + } + case "web_app_stop_gyroscope": { + final BotSensors sensors = delegate.getBotSensors(); + if (sensors != null && sensors.stopGyroscope()) { + notifyEvent("gyroscope_stopped", null); + } else { + notifyEvent("gyroscope_failed", obj("error", "UNSUPPORTED")); + } + break; + } + case "web_app_start_device_orientation": { + final BotSensors sensors = delegate.getBotSensors(); + long refresh_rate = 1000; + boolean absolute = false; + try { + JSONObject json = new JSONObject(eventData); + refresh_rate = json.getLong("refresh_rate"); + absolute = json.optBoolean("need_absolute", false); + } catch (Exception e) {} + refresh_rate = Utilities.clamp(refresh_rate, 1000, 20); + if (sensors != null && sensors.startOrientation(absolute, refresh_rate)) { + notifyEvent("device_orientation_started", null); + } else { + notifyEvent("device_orientation_failed", obj("error", "UNSUPPORTED")); + } + break; + } + case "web_app_stop_device_orientation": { + final BotSensors sensors = delegate.getBotSensors(); + if (sensors != null && sensors.stopOrientation()) { + notifyEvent("device_orientation_stopped", null); + } else { + notifyEvent("device_orientation_failed", obj("error", "UNSUPPORTED")); + } + break; + } + case "web_app_add_to_home_screen": { + if (isRequestingPageOpen || botUser == null || System.currentTimeMillis() - lastClickMs > 10_000) { + return; + } + if (MediaDataController.getInstance(currentAccount).isShortcutAdded(botUser.id, MediaDataController.SHORTCUT_TYPE_ATTACHED_BOT)) { + notifyEvent("home_screen_added", null); + return; + } + MediaDataController.getInstance(currentAccount).installShortcut(botUser.id, MediaDataController.SHORTCUT_TYPE_ATTACHED_BOT, result -> { + if (result) { + notifyEvent("home_screen_added", null); + } else { + notifyEvent("home_screen_failed", obj("error", "UNSUPPORTED")); + } + }); + break; + } + case "web_app_check_home_screen": { + notifyEvent("home_screen_checked", obj( + "status", botUser != null && Build.VERSION.SDK_INT >= 26 ? ( + MediaDataController.getInstance(currentAccount).isShortcutAdded(botUser.id, MediaDataController.SHORTCUT_TYPE_ATTACHED_BOT) ? "added" : "missed" + ) : "unsupported" + )); + break; + } + case "web_app_set_emoji_status": { + if (isRequestingPageOpen || botUser == null || System.currentTimeMillis() - lastClickMs > 10_000) { + return; + } + long custom_emoji_id = 0; + int duration = 0; + try { + JSONObject o = new JSONObject(eventData); + custom_emoji_id = Long.parseLong(o.getString("custom_emoji_id")); + duration = o.getInt("duration"); + } catch (Exception e) {} + if (botUser == null) { + notifyEvent("emoji_status_failed", obj("error", "UNKNOWN_ERROR")); + return; + } + SetupEmojiStatusSheet.show(currentAccount, botUser, custom_emoji_id, duration, (error, document) -> { + if (error == null) { + notifyEvent("emoji_status_set", null); + if (delegate != null) { + delegate.onEmojiStatusSet(document); + } + } else { + notifyEvent("emoji_status_failed", obj("error", error)); + } + }); + break; + } + case "web_app_request_emoji_status_access": { + if (isRequestingPageOpen || botUser == null || System.currentTimeMillis() - lastClickMs > 10_000) { + return; + } + SetupEmojiStatusSheet.askPermission(currentAccount, botUser.id, (shownDialog, status) -> { + notifyEmojiStatusAccess(status); + if (shownDialog && "allowed".equalsIgnoreCase(status) && delegate != null) { + delegate.onEmojiStatusGranted(true); + } + }); + break; + } + case "web_app_request_safe_area": { + reportSafeInsets(lastInsets, true); + break; + } + case "web_app_request_content_safe_area": { + reportSafeContentInsets(lastInsetsTopMargin, true); + break; + } + case "web_app_request_location": { + if (isRequestingPageOpen || botUser == null) { + return; + } + if (location == null) { + location = BotLocation.get(getContext(), currentAccount, botUser.id); + location.listen(this.notifyLocationChecked); + } + if (!location.granted()) { + location.request((now, granted) -> { + if (delegate != null && now) { + delegate.onLocationGranted(granted); + } + location.requestObject(obj -> { + notifyEvent("location_requested", obj); + }); + }); + } else { + location.requestObject(obj -> { + notifyEvent("location_requested", obj); + }); + } + break; + } + case "web_app_check_location": { + if (location == null) { + location = BotLocation.get(getContext(), currentAccount, botUser.id); + location.listen(this.notifyLocationChecked); + } + notifyLocationChecked.run(); + break; + } + case "web_app_open_location_settings": { + if (isRequestingPageOpen || botUser == null || System.currentTimeMillis() - lastClickMs > 10_000) { + return; + } + + lastClickMs = 0; + + BaseFragment lastFragment = LaunchActivity.getSafeLastFragment(); + if (lastFragment == null || lastFragment.getParentLayout() == null) return; + final INavigationLayout parentLayout = lastFragment.getParentLayout(); + lastFragment.presentFragment(ProfileActivity.of(botUser.id)); + AndroidUtilities.scrollToFragmentRow(parentLayout, "botPermissionLocation"); + if (delegate != null) { + delegate.onCloseToTabs(); + } + + break; + } + case "web_app_request_file_download": { + if (isRequestingPageOpen || botUser == null || System.currentTimeMillis() - lastClickMs > 10_000) { + return; + } + + if (downloads == null) { + downloads = BotDownloads.get(getContext(), currentAccount, botUser.id); + } + String url, file_name; + try { + JSONObject o = new JSONObject(eventData); + url = o.getString("url"); + file_name = o.getString("file_name"); + } catch (Exception e) { + FileLog.e(e); + notifyEvent("file_download_requested", obj("status", "cancelled")); + return; + } + if (downloads.getCached(url) != null) { + downloads.download(url, file_name); + notifyEvent("file_download_requested", obj("status", "downloading")); + return; + } + + final String finalUrl = url; + final String finalFileName = file_name; + final TL_bots.checkDownloadFileParams req = new TL_bots.checkDownloadFileParams(); + req.bot = MessagesController.getInstance(currentAccount).getInputUser(botUser); + req.file_name = file_name; + req.url = url; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (!(res instanceof TLRPC.TL_boolTrue)) { + notifyEvent("file_download_requested", obj("status", "cancelled")); + return; + } + BotDownloads.showAlert(getContext(), finalUrl, finalFileName, UserObject.getUserName(botUser), status -> { + if (!status) { + notifyEvent("file_download_requested", obj("status", "cancelled")); + return; + } + + downloads.download(finalUrl, finalFileName); + notifyEvent("file_download_requested", obj("status", "downloading")); + }); + })); + break; + } + case "web_app_send_prepared_message": { + if (isRequestingPageOpen || botUser == null || System.currentTimeMillis() - lastClickMs > 10_000) { + return; + } + + String id = null; + try { + JSONObject o = new JSONObject(eventData); + id = o.getString("id"); + } catch (Exception e) { + FileLog.e(e); + notifyEvent("prepared_message_failed", obj("error", "MESSAGE_EXPIRED")); + return; + } + if (TextUtils.isEmpty(id)) { + notifyEvent("prepared_message_failed", obj("error", "MESSAGE_EXPIRED")); + return; + } + + BotShareSheet.share(getContext(), currentAccount, botUser.id, id, resourcesProvider, () -> { + if (delegate != null) { + delegate.onCloseToTabs(); + } + LaunchActivity.dismissAllWeb(); + }, (error, dialogIds) -> { + if (TextUtils.isEmpty(error)) { + notifyEvent("prepared_message_sent", null); + if (delegate != null) { + delegate.onOpenBackFromTabs(); + } + AndroidUtilities.runOnUIThread(() -> { + if (proxy != null && proxy.container != null && proxy.container.delegate != null) { + proxy.container.delegate.onSharedTo(dialogIds); + } + }, 500); + } else { + notifyEvent("prepared_message_failed", obj("error", error)); + } + }); + break; + } + case "web_app_toggle_orientation_lock": { + boolean locked = false; + try { + JSONObject o = new JSONObject(eventData); + locked = o.getBoolean("locked"); + } catch (Exception e) {} + if (delegate != null) { + delegate.onOrientationLockChanged(locked); + } + break; + } default: { FileLog.d("unknown webapp event " + eventType); break; @@ -2152,12 +2583,45 @@ public boolean isLightStatusBar() { } } + private final Rect lastInsets = new Rect(0, 0, 0, 0); + private int lastInsetsTopMargin = 0; + public void reportSafeInsets(Rect insets, int topContentMargin) { + reportSafeInsets(insets, false); + reportSafeContentInsets(topContentMargin, false); + } + private void reportSafeInsets(Rect insets, boolean force) { + if (insets == null || !force && lastInsets.equals(insets)) + return; + notifyEvent("safe_area_changed", obj( + "left", insets.left / AndroidUtilities.density, + "top", insets.top / AndroidUtilities.density, + "right", insets.right / AndroidUtilities.density, + "bottom", insets.bottom / AndroidUtilities.density + )); + lastInsets.set(insets); + } + private void reportSafeContentInsets(int topContentMargin, boolean force) { + if (!force && topContentMargin == lastInsetsTopMargin) + return; + notifyEvent("content_safe_area_changed", obj( + "left", 0, + "top", topContentMargin / AndroidUtilities.density, + "right", 0, + "bottom", 0 + )); + lastInsetsTopMargin = topContentMargin; + } + + public void notifyEmojiStatusAccess(String status) { + notifyEvent("emoji_status_access_requested", obj("status", status)); + } + private void createBiometry() { if (botUser == null) { return; } if (biometry == null) { - biometry = new BotBiometry(getContext(), currentAccount, botUser.id); + biometry = BotBiometry.get(getContext(), currentAccount, botUser.id); } else { biometry.load(); } @@ -2190,6 +2654,10 @@ private void error(String reason) { BulletinFactory.of(this, resourcesProvider).createSimpleBulletin(R.raw.error, reason).show(); } + private final Runnable notifyLocationChecked = () -> { + notifyEvent("location_checked", location.checkObject()); + }; + private int lastDialogType = -1; private int shownDialogsCount = 0; private long blockedDialogsUntil; @@ -2294,7 +2762,11 @@ private String hexFixed(int h) { return hex; } - public void onWebViewCreated() { + public void onWebViewCreated(MyWebView webView) { + + } + + public void onWebViewDestroyed(MyWebView webView) { } @@ -2306,16 +2778,25 @@ public BotWebViewProxy(BotWebViewContainer container) { public void setContainer(BotWebViewContainer container) { this.container = container; } + @Keep @JavascriptInterface public void postEvent(String eventType, String eventData) { - if (container == null) { - FileLog.d("webviewproxy.postEvent: no container"); - return; + try { + if (container == null) { + FileLog.d("webviewproxy.postEvent: no container"); + return; + } + AndroidUtilities.runOnUIThread(() -> { + try { + if (container == null) return; + container.onEventReceived(this, eventType, eventData); + } catch (Exception e2) { + FileLog.e(e2); + } + }); + } catch (Exception e) { + FileLog.e(e); } - AndroidUtilities.runOnUIThread(() -> { - if (container == null) return; - container.onEventReceived(eventType, eventData); - }); } } @@ -2332,6 +2813,7 @@ public void setContainer(BotWebViewContainer container) { this.container = container; } + @Keep @JavascriptInterface public void post(String type, String data) { if (container == null) return; @@ -2341,6 +2823,7 @@ public void post(String type, String data) { }); } + @Keep @JavascriptInterface public void resolveShare(String json, byte[] file, String fileName, String fileMimeType) { AndroidUtilities.runOnUIThread(() -> { @@ -2451,6 +2934,10 @@ public interface Delegate { default void onInstantClose() { onCloseRequested(null); }; default void onCloseToTabs() { onCloseRequested(null); }; + default void onOpenBackFromTabs() {} + default void onSharedTo(ArrayList dialogIds) {} + + default void onOrientationLockChanged(boolean locked) {} /** * Called when WebView requests to change closing behavior @@ -2481,6 +2968,10 @@ default void onSendWebViewData(String data) {} default void onWebAppBackgroundChanged(boolean actionBarColor, int color) {}; + default void onLocationGranted(boolean granted) {} + default void onEmojiStatusGranted(boolean granted) {} + default void onEmojiStatusSet(TLRPC.Document document) {} + /** * Called when WebView requests to set background color * @@ -2540,6 +3031,14 @@ default boolean isClipboardAvailable() { default String getWebAppName() { return null; } + + default String onFullscreenRequested(boolean fullscreen) { + return "UNSUPPORTED"; + } + + default BotSensors getBotSensors() { + return null; + } } public final static class PopupButton { @@ -2684,6 +3183,8 @@ public static class MyWebView extends WebView { public String urlFallback = "about:blank"; public boolean dangerousUrl; + private BottomSheet currentSheet; + public DangerousWebWarning currentWarning; public boolean isPageLoaded() { return isPageLoaded; @@ -2722,7 +3223,9 @@ public boolean onLongClick(View v) { try { try { Uri uri = Uri.parse(formattedUrl); - formattedUrl = Browser.replaceHostname(uri, Browser.IDN_toUnicode(uri.getHost()), null); + if (uri != null && !uri.getScheme().equalsIgnoreCase("data")) { + formattedUrl = Browser.replaceHostname(uri, Browser.IDN_toUnicode(uri.getHost()), null); + } } catch (Exception e) { FileLog.e(e, false); } @@ -2756,7 +3259,7 @@ public boolean onLongClick(View v) { } } }); - builder.show(); + currentSheet = builder.show(); }); return true; @@ -2827,7 +3330,7 @@ public boolean onLongClick(View v) { } } }); - builder.show(); + currentSheet = builder.show(); }); return true; @@ -3076,6 +3579,11 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) { @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { + getSettings().setMediaPlaybackRequiresUserGesture(true); + if (currentSheet != null) { + currentSheet.dismiss(); + currentSheet = null; + } currentHistoryEntry = null; currentUrl = url; lastSiteName = null; @@ -3789,6 +4297,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { botWebViewContainer.lastClickMs = System.currentTimeMillis(); + getSettings().setMediaPlaybackRequiresUserGesture(false); } return super.onTouchEvent(event); } @@ -3815,6 +4324,10 @@ public void destroy() { @Override public void loadUrl(@NonNull String url) { + if (currentSheet != null) { + currentSheet.dismiss(); + currentSheet = null; + } final String ourl = url; checkCachedMetaProperties(url); openedByUrl = url; @@ -3832,6 +4345,10 @@ public void loadUrl(@NonNull String url) { @Override public void loadUrl(@NonNull String url, @NonNull Map additionalHttpHeaders) { + if (currentSheet != null) { + currentSheet.dismiss(); + currentSheet = null; + } final String ourl = url; checkCachedMetaProperties(url); openedByUrl = url; @@ -3845,6 +4362,10 @@ public void loadUrl(@NonNull String url, @NonNull Map additional } public void loadUrl(String url, WebMetadataCache.WebMetadata meta) { + if (currentSheet != null) { + currentSheet.dismiss(); + currentSheet = null; + } final String ourl = url; applyCachedMeta(meta); openedByUrl = url; @@ -4071,4 +4592,50 @@ public static String magic2tonsite(String url) { if (tonsite_host == null) return url; return Browser.replace(Uri.parse(url), "tonsite", null, tonsite_host, null); } + + private static JSONObject obj(String key1, Object value) { + try { + JSONObject obj = new JSONObject(); + obj.put(key1, value); + return obj; + } catch (Exception e) { + return null; + } + } + + private static JSONObject obj(String key1, Object value, String key2, Object value2) { + try { + JSONObject obj = new JSONObject(); + obj.put(key1, value); + obj.put(key2, value2); + return obj; + } catch (Exception e) { + return null; + } + } + + private static JSONObject obj(String key1, Object value, String key2, Object value2, String key3, Object value3) { + try { + JSONObject obj = new JSONObject(); + obj.put(key1, value); + obj.put(key2, value2); + obj.put(key3, value3); + return obj; + } catch (Exception e) { + return null; + } + } + + private static JSONObject obj(String key1, Object value, String key2, Object value2, String key3, Object value3, String key4, Object value4) { + try { + JSONObject obj = new JSONObject(); + obj.put(key1, value); + obj.put(key2, value2); + obj.put(key3, value3); + obj.put(key4, value4); + return obj; + } catch (Exception e) { + return null; + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/web/HttpGetFileTask.java b/TMessagesProj/src/main/java/org/telegram/ui/web/HttpGetFileTask.java index 36dcae69a9f..6baa06ef297 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/web/HttpGetFileTask.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/web/HttpGetFileTask.java @@ -3,6 +3,7 @@ import android.content.ContentResolver; import android.os.AsyncTask; +import android.os.Build; import android.webkit.MimeTypeMap; import android.webkit.URLUtil; @@ -23,13 +24,25 @@ public class HttpGetFileTask extends AsyncTask { + private File file; private Utilities.Callback callback; private Exception exception; + private long max_size = -1; public HttpGetFileTask(Utilities.Callback callback) { this.callback = callback; } + public HttpGetFileTask setDestFile(File file) { + this.file = file; + return this; + } + + public HttpGetFileTask setMaxSize(long max_size) { + this.max_size = max_size; + return this; + } + @Override protected File doInBackground(String... params) { String urlString = params[0]; @@ -48,8 +61,23 @@ protected File doInBackground(String... params) { in = urlConnection.getErrorStream(); } - String ext = MimeTypeMap.getSingleton().getExtensionFromMimeType(urlConnection.getContentType()); - File file = StoryEntry.makeCacheFile(UserConfig.selectedAccount, ext); + urlConnection.getResponseCode(); + long size; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + size = urlConnection.getContentLengthLong(); + } else { + size = urlConnection.getContentLength(); + } + if (max_size > 0 && size > max_size) { + in.close(); + if (file != null) file = null; + return null; + } + + if (file == null) { + final String ext = MimeTypeMap.getSingleton().getExtensionFromMimeType(urlConnection.getContentType()); + file = StoryEntry.makeCacheFile(UserConfig.selectedAccount, ext); + } BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(file)); byte[] buffer = new byte[1024]; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/web/WebInstantView.java b/TMessagesProj/src/main/java/org/telegram/ui/web/WebInstantView.java index ce78dc60e88..6bddb87891e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/web/WebInstantView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/web/WebInstantView.java @@ -25,6 +25,8 @@ import android.webkit.WebViewClient; import android.widget.FrameLayout; +import androidx.annotation.Keep; + import com.google.common.collect.Lists; import org.json.JSONArray; @@ -434,6 +436,7 @@ public WebResourceResponse shouldInterceptRequest(WebView view, String url) { webViewContainer.addView(webView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); final boolean[] done = new boolean[] { false }; webView.addJavascriptInterface(new Object() { + @Keep @JavascriptInterface public void done(String json) { AndroidUtilities.runOnUIThread(() -> { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/web/WebMetadataCache.java b/TMessagesProj/src/main/java/org/telegram/ui/web/WebMetadataCache.java index 7d792e9f260..5950bee2c78 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/web/WebMetadataCache.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/web/WebMetadataCache.java @@ -19,6 +19,8 @@ import android.webkit.WebViewClient; import android.widget.FrameLayout; +import androidx.annotation.Keep; + import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.BuildVars; @@ -293,6 +295,7 @@ private static class SitenameProxy { public SitenameProxy(Utilities.Callback whenReceived) { this.whenReceived = whenReceived; } + @Keep @JavascriptInterface public void post(String type, String data) { AndroidUtilities.runOnUIThread(() -> { diff --git a/TMessagesProj/src/main/res/drawable-hdpi/filled_access_fingerprint.png b/TMessagesProj/src/main/res/drawable-hdpi/filled_access_fingerprint.png new file mode 100644 index 00000000000..ace597a8cad Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/filled_access_fingerprint.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/filled_access_location.png b/TMessagesProj/src/main/res/drawable-hdpi/filled_access_location.png new file mode 100644 index 00000000000..f3b03941251 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/filled_access_location.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/filled_access_sleeping.png b/TMessagesProj/src/main/res/drawable-hdpi/filled_access_sleeping.png new file mode 100644 index 00000000000..28fb733993c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/filled_access_sleeping.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/menu_download_round.png b/TMessagesProj/src/main/res/drawable-hdpi/menu_download_round.png new file mode 100644 index 00000000000..b487ee55a0e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/menu_download_round.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/menu_edited_stamp.png b/TMessagesProj/src/main/res/drawable-hdpi/menu_edited_stamp.png new file mode 100644 index 00000000000..bc29a931eaf Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/menu_edited_stamp.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/menu_forward_stamp.png b/TMessagesProj/src/main/res/drawable-hdpi/menu_forward_stamp.png new file mode 100644 index 00000000000..0767f5883a8 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/menu_forward_stamp.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/menu_sendfile_plus.png b/TMessagesProj/src/main/res/drawable-hdpi/menu_sendfile_plus.png new file mode 100644 index 00000000000..9582667be00 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/menu_sendfile_plus.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/menu_video_chromecast.png b/TMessagesProj/src/main/res/drawable-hdpi/menu_video_chromecast.png new file mode 100644 index 00000000000..131ed2bf6f0 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/menu_video_chromecast.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/menu_video_loop.png b/TMessagesProj/src/main/res/drawable-hdpi/menu_video_loop.png new file mode 100644 index 00000000000..b89cdd4abdc Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/menu_video_loop.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/menu_video_pip.png b/TMessagesProj/src/main/res/drawable-hdpi/menu_video_pip.png new file mode 100644 index 00000000000..eae78242faa Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/menu_video_pip.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/nav_edit_attach.png b/TMessagesProj/src/main/res/drawable-hdpi/nav_edit_attach.png new file mode 100644 index 00000000000..6a633a37c2c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/nav_edit_attach.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/filled_access_fingerprint.png b/TMessagesProj/src/main/res/drawable-mdpi/filled_access_fingerprint.png new file mode 100644 index 00000000000..67ec316c6b5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/filled_access_fingerprint.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/filled_access_location.png b/TMessagesProj/src/main/res/drawable-mdpi/filled_access_location.png new file mode 100644 index 00000000000..5761f0f4c94 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/filled_access_location.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/filled_access_sleeping.png b/TMessagesProj/src/main/res/drawable-mdpi/filled_access_sleeping.png new file mode 100644 index 00000000000..dea97d3520d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/filled_access_sleeping.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/menu_download_round.png b/TMessagesProj/src/main/res/drawable-mdpi/menu_download_round.png new file mode 100644 index 00000000000..3b5624e3932 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/menu_download_round.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/menu_edited_stamp.png b/TMessagesProj/src/main/res/drawable-mdpi/menu_edited_stamp.png new file mode 100644 index 00000000000..f7f2eb1b289 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/menu_edited_stamp.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/menu_forward_stamp.png b/TMessagesProj/src/main/res/drawable-mdpi/menu_forward_stamp.png new file mode 100644 index 00000000000..104d740e5c2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/menu_forward_stamp.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/menu_sendfile_plus.png b/TMessagesProj/src/main/res/drawable-mdpi/menu_sendfile_plus.png new file mode 100644 index 00000000000..432f931022b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/menu_sendfile_plus.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/menu_video_chromecast.png b/TMessagesProj/src/main/res/drawable-mdpi/menu_video_chromecast.png new file mode 100644 index 00000000000..d6783bcf76e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/menu_video_chromecast.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/menu_video_loop.png b/TMessagesProj/src/main/res/drawable-mdpi/menu_video_loop.png new file mode 100644 index 00000000000..b941b994035 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/menu_video_loop.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/menu_video_pip.png b/TMessagesProj/src/main/res/drawable-mdpi/menu_video_pip.png new file mode 100644 index 00000000000..53474c1153e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/menu_video_pip.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/nav_edit_attach.png b/TMessagesProj/src/main/res/drawable-mdpi/nav_edit_attach.png new file mode 100644 index 00000000000..2e7d827ae2b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/nav_edit_attach.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/filled_access_fingerprint.png b/TMessagesProj/src/main/res/drawable-xhdpi/filled_access_fingerprint.png new file mode 100644 index 00000000000..18f3e6cc396 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/filled_access_fingerprint.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/filled_access_location.png b/TMessagesProj/src/main/res/drawable-xhdpi/filled_access_location.png new file mode 100644 index 00000000000..b9a477f621c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/filled_access_location.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/filled_access_sleeping.png b/TMessagesProj/src/main/res/drawable-xhdpi/filled_access_sleeping.png new file mode 100644 index 00000000000..4389240a81f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/filled_access_sleeping.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/menu_download_round.png b/TMessagesProj/src/main/res/drawable-xhdpi/menu_download_round.png new file mode 100644 index 00000000000..dbc598584a4 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/menu_download_round.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/menu_edited_stamp.png b/TMessagesProj/src/main/res/drawable-xhdpi/menu_edited_stamp.png new file mode 100644 index 00000000000..7b35eb12a62 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/menu_edited_stamp.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/menu_forward_stamp.png b/TMessagesProj/src/main/res/drawable-xhdpi/menu_forward_stamp.png new file mode 100644 index 00000000000..17cc5aa4bb7 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/menu_forward_stamp.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/menu_sendfile_plus.png b/TMessagesProj/src/main/res/drawable-xhdpi/menu_sendfile_plus.png new file mode 100644 index 00000000000..bea53e7b07f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/menu_sendfile_plus.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/menu_video_chromecast.png b/TMessagesProj/src/main/res/drawable-xhdpi/menu_video_chromecast.png new file mode 100644 index 00000000000..e39f24e716d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/menu_video_chromecast.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/menu_video_loop.png b/TMessagesProj/src/main/res/drawable-xhdpi/menu_video_loop.png new file mode 100644 index 00000000000..2bb13d55dfe Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/menu_video_loop.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/menu_video_pip.png b/TMessagesProj/src/main/res/drawable-xhdpi/menu_video_pip.png new file mode 100644 index 00000000000..cc34fe79b49 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/menu_video_pip.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/nav_edit_attach.png b/TMessagesProj/src/main/res/drawable-xhdpi/nav_edit_attach.png new file mode 100644 index 00000000000..6c9cfc729c6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/nav_edit_attach.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/filled_access_fingerprint.png b/TMessagesProj/src/main/res/drawable-xxhdpi/filled_access_fingerprint.png new file mode 100644 index 00000000000..b470f69c51f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/filled_access_fingerprint.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/filled_access_location.png b/TMessagesProj/src/main/res/drawable-xxhdpi/filled_access_location.png new file mode 100644 index 00000000000..12fef07a84e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/filled_access_location.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/filled_access_sleeping.png b/TMessagesProj/src/main/res/drawable-xxhdpi/filled_access_sleeping.png new file mode 100644 index 00000000000..b6a435b20c1 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/filled_access_sleeping.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/menu_download_round.png b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_download_round.png new file mode 100644 index 00000000000..558305d71c8 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_download_round.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/menu_edited_stamp.png b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_edited_stamp.png new file mode 100644 index 00000000000..f2778e207cb Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_edited_stamp.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/menu_forward_stamp.png b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_forward_stamp.png new file mode 100644 index 00000000000..1c079c39581 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_forward_stamp.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/menu_sendfile_plus.png b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_sendfile_plus.png new file mode 100644 index 00000000000..a8afa83df3d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_sendfile_plus.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/menu_video_chromecast.png b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_video_chromecast.png new file mode 100644 index 00000000000..b8680d7be4d Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_video_chromecast.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/menu_video_loop.png b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_video_loop.png new file mode 100644 index 00000000000..65fa4579b23 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_video_loop.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/menu_video_pip.png b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_video_pip.png new file mode 100644 index 00000000000..570a69780b8 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_video_pip.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/nav_edit_attach.png b/TMessagesProj/src/main/res/drawable-xxhdpi/nav_edit_attach.png new file mode 100644 index 00000000000..1ac236a056f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/nav_edit_attach.png differ diff --git a/TMessagesProj/src/main/res/drawable/filled_paid_broadcast.xml b/TMessagesProj/src/main/res/drawable/filled_paid_broadcast.xml new file mode 100644 index 00000000000..4482b5f58c6 --- /dev/null +++ b/TMessagesProj/src/main/res/drawable/filled_paid_broadcast.xml @@ -0,0 +1,10 @@ + + + + diff --git a/TMessagesProj/src/main/res/raw/convert_video.json b/TMessagesProj/src/main/res/raw/convert_video.json new file mode 100644 index 00000000000..5d698852d75 --- /dev/null +++ b/TMessagesProj/src/main/res/raw/convert_video.json @@ -0,0 +1 @@ +{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":38,"w":512,"h":512,"nm":"Convert 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Triangle","sr":1,"ks":{"p":{"a":0,"k":[252.644,256.001,0]},"a":{"a":0,"k":[-19.5,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":0,"s":[20,20,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":11,"s":[155,155,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":21,"s":[85,85,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":29,"s":[106,106,100]},{"t":37,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,3.463],[0,0],[-9.12,0],[-2.81,-2.023],[0,0],[5.329,-7.401],[1.445,-1.04],[0,0],[5.329,7.401]],"o":[[0,0],[0,-9.12],[3.463,0],[0,0],[7.401,5.329],[-1.04,1.445],[0,0],[-7.401,5.329],[-2.023,-2.81]],"v":[[-62.933,66.842],[-62.933,-66.843],[-46.419,-83.357],[-36.77,-80.245],[56.067,-13.402],[59.819,9.648],[56.067,13.401],[-36.77,80.243],[-59.821,76.491]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Path","bm":0,"hd":false}],"ip":1,"op":180,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Arrow L","parent":4,"sr":1,"ks":{"p":{"a":0,"k":[-174.39,30.275,0]},"a":{"a":0,"k":[0,35.594,0]},"s":{"a":1,"k":[{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":11,"s":[25,65,100]},{"t":21,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.523,-0.632],[0,0],[2.78,-2.3],[1.521,0],[0,0],[0,3.608],[-0.969,1.171],[0,0],[-5.559,-4.6]],"o":[[0,0],[2.3,2.78],[-1.171,0.969],[0,0],[-3.608,0],[0,-1.521],[0,0],[4.6,-5.559],[0.632,0.523]],"v":[[10.066,-30.858],[56.203,24.896],[55.335,34.094],[51.17,35.594],[-51.171,35.594],[-57.703,29.061],[-56.204,24.896],[-10.066,-30.858],[8.33,-32.594]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Triangle","bm":0,"hd":false}],"ip":11,"op":180,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Arrow R","parent":4,"sr":1,"ks":{"p":{"a":0,"k":[174.39,-30.275,0]},"a":{"a":0,"k":[0,-35.594,0]},"s":{"a":1,"k":[{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":11,"s":[25,65,100]},{"t":21,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.523,0.632],[0,0],[2.78,2.3],[1.521,0],[0,0],[0,-3.608],[-0.969,-1.171],[0,0],[-5.559,4.6]],"o":[[0,0],[2.3,-2.78],[-1.171,-0.969],[0,0],[-3.608,0],[0,1.521],[0,0],[4.6,5.559],[0.632,-0.523]],"v":[[10.066,30.858],[56.203,-24.896],[55.335,-34.094],[51.17,-35.594],[-51.171,-35.594],[-57.703,-29.061],[-56.204,-24.896],[-10.066,30.858],[8.33,32.594]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Triangle","bm":0,"hd":false}],"ip":11,"op":180,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Lines","sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.7],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":11,"s":[-134]},{"i":{"x":[0.7],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":29,"s":[9]},{"t":37,"s":[0]}]},"p":{"a":0,"k":[256.244,256,0]},"a":{"a":0,"k":[0.244,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.6,0.6,0.6],"y":[0,0,0]},"t":11,"s":[120,120,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":21,"s":[95,95,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":29,"s":[102,102,100]},{"t":37,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.6,"y":0},"t":11,"s":[{"i":[[0,0],[98.646,0],[0,0]],"o":[[0,-100.322],[-0.021,0.018],[0,0]],"v":[[178.857,0],[0.244,-181.649],[-0.393,-182.242]],"c":false}]},{"i":{"x":0.8,"y":1},"o":{"x":0.167,"y":0.167},"t":16,"s":[{"i":[[0,0],[98.646,0],[23.376,-6.196]],"o":[[-0.824,-88.475],[-19.683,0.012],[0,0]],"v":[[173.875,-10.469],[0.244,-181.649],[-57.019,-173.617]],"c":false}]},{"t":29,"s":[{"i":[[0,0],[98.645,0],[31.929,-49.364]],"o":[[0,-100.322],[-62.443,0],[0,0]],"v":[[178.857,0],[0.244,-181.649],[-149.087,-99.697]],"c":false}]}]},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.6,"y":0},"t":11,"s":[{"i":[[0,0],[-98.645,0],[0,0]],"o":[[0,100.322],[0.168,0.56],[0,0]],"v":[[-178.369,0],[0.244,181.649],[2.252,181.822]],"c":false}]},{"i":{"x":0.8,"y":1},"o":{"x":0.167,"y":0.167},"t":16,"s":[{"i":[[0,0],[-98.645,0],[-8.386,5.043]],"o":[[7.214,91.295],[19.296,0.384],[0,0]],"v":[[-175.037,13.23],[0.244,181.649],[53.988,171.98]],"c":false}]},{"t":29,"s":[{"i":[[0,0],[-98.645,0],[-32.242,47.317]],"o":[[0,100.322],[60.893,0],[0,0]],"v":[[-178.369,0],[0.244,181.649],[147.155,103.342]],"c":false}]}]},"nm":"Path 2","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":34},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Shape","bm":0,"hd":false}],"ip":11,"op":180,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Circle","parent":4,"sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[-107]},{"t":11,"s":[0]}]},"p":{"a":0,"k":[0,0.044,0]},"s":{"a":1,"k":[{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":0,"s":[25,25,100]},{"t":11,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[0,0],[-0.879,0.207],[3.793,89.685]],"o":[[0,0],[-20.267,-17.641],[-1.408,-33.297]],"v":[[-111.79,140.969],[-111.558,141.134],[-180.382,0]],"c":false}]},{"t":11,"s":[{"i":[[0,0],[-0.879,0.207],[0,100.346]],"o":[[0,0],[-99.623,0],[0,-33.326]],"v":[[-0.232,181.528],[0,181.693],[-180.382,0]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":34},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.4,"y":1},"o":{"x":0.6,"y":0},"t":0,"s":[{"i":[[0,0],[-0.707,-0.038],[-4.276,-93.164]],"o":[[0,0],[19.025,18.343],[1.598,34.817]],"v":[[111.216,-141.781],[112.793,-141.989],[180.383,0]],"c":false}]},{"t":11,"s":[{"i":[[0,0],[-0.707,-0.038],[0,-100.346]],"o":[[0,0],[99.622,0],[0,34.854]],"v":[[-1.578,-181.485],[0,-181.693],[180.383,0]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":34},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":1,"op":12,"st":0,"bm":0}]} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/seek_speed_hint.json b/TMessagesProj/src/main/res/raw/seek_speed_hint.json new file mode 100644 index 00000000000..9911aaee13b --- /dev/null +++ b/TMessagesProj/src/main/res/raw/seek_speed_hint.json @@ -0,0 +1 @@ +{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":42,"w":512,"h":512,"nm":"Move between stories","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0},"p":{"a":0,"k":[254,241,0]},"s":{"a":0,"k":[94,94,100]}},"ao":0,"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"L 2","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.6],"y":[0]},"t":11,"s":[0]},{"t":21,"s":[100]}]},"p":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.7,"y":0},"t":11,"s":[-256.122,-96.904,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":28,"s":[-177.766,-89.786,0],"to":[0,0,0],"ti":[0,0,0]},{"t":40,"s":[-203.848,-89.786,0]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.8,"y":1},"o":{"x":0.7,"y":0},"t":11,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-8.723,16.786],[8.723,1.256],[-8.219,-16.786]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":28,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[49.848,59.366],[-31.15,4.442],[47.508,-59.366]],"c":false}]},{"t":40,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[31.15,59.366],[-31.15,4.442],[29.35,-59.366]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":26},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":16,"op":180,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"L","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.7],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":17,"s":[100]},{"i":{"x":[0.7],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":21,"s":[50]},{"t":25,"s":[100]}]},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[-203.848,-89.786,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.8,"y":1},"o":{"x":0.7,"y":0},"t":11,"s":[-225.164,-89.786,0],"to":[234.046,-50.514,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":28,"s":[225.55,-89.786,0],"to":[0,0,0],"ti":[0,0,0]},{"t":40,"s":[203.282,-89.786,0]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[31.15,59.366],[-31.15,4.442],[29.35,-59.366]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":11,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[21.576,59.265],[-31.15,4.442],[19.776,-59.467]],"c":false}]},{"i":{"x":0.647,"y":1},"o":{"x":0.167,"y":0.167},"t":23,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[1.267,27.364],[4.53,2.048],[1.361,-27.364]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":28,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-13.865,59.424],[29.848,4.442],[-12.391,-59.308]],"c":false}]},{"t":40,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-30.85,59.214],[31.082,3.819],[-29.65,-59.464]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":26},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"R","parent":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[100]},{"t":26,"s":[0]}]},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[203.282,-89.786,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.8,"y":1},"o":{"x":0.7,"y":0},"t":11,"s":[181.965,-89.786,0],"to":[0,0,0],"ti":[0,0,0]},{"t":28,"s":[297.019,-89.786,0]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-29.35,-59.366],[31.15,4.442],[-31.15,59.366]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":11,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-39.988,-59.467],[31.15,4.442],[-41.788,59.265]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":22,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3.644,-38.875],[3.868,2.909],[-3.868,38.875]],"c":false}]},{"i":{"x":0.8,"y":1},"o":{"x":0.167,"y":0.167},"t":24,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[2.837,-18.475],[-3.011,1.382],[3.011,18.475]],"c":false}]},{"t":28,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[7.272,-13.589],[-7.718,1.017],[7.718,13.589]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":26},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":25,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Finger","parent":6,"sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":1,"s":[0]},{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":12,"s":[-9]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":29,"s":[3]},{"t":41,"s":[0]}]},"p":{"a":0,"k":[-26.234,-16.549,0]},"a":{"a":0,"k":[28.352,82.368,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.3,"y":0},"t":1,"s":[{"i":[[0.605,2.359],[7.727,29.998],[8.683,-10.722],[-2.086,-8.159],[0,0]],"o":[[-7.42,-28.87],[-4.394,-16.705],[-2.747,3.392],[0,0],[0.547,2.933]],"v":[[39.186,57.705],[0.616,-92.483],[-34.371,-99.389],[-38.555,-82.695],[10.429,106.432]],"c":false}]},{"i":{"x":0.8,"y":1},"o":{"x":0.7,"y":0},"t":12,"s":[{"i":[[0.863,2.277],[6.893,41.194],[9.446,-10.056],[-0.145,-9.755],[-10.932,-42.059]],"o":[[-17.677,-46.632],[-3.158,-16.982],[-2.988,3.181],[1.363,54.018],[3.933,15.133]],"v":[[45.998,48.158],[11.488,-91.568],[-22.899,-101.02],[-29.75,-83.818],[10.429,106.432]],"c":false}]},{"i":{"x":0.8,"y":1},"o":{"x":0.2,"y":0},"t":22,"s":[{"i":[[0.342,2.411],[17.249,30.876],[6.934,-11.928],[-3.314,-7.742],[0.034,-60.632]],"o":[[-8.565,-60.465],[-8.294,-13.639],[-2.193,3.773],[0,0],[-0.006,11.229]],"v":[[43.368,55.393],[-9.869,-89.812],[-49.107,-91.498],[-50.449,-72.351],[6.707,95.096]],"c":false}]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":29,"s":[{"i":[[0.725,2.325],[5.483,33.664],[9.726,-9.786],[-1.248,-8.329],[-36.791,-104.933]],"o":[[-9.727,-31.189],[-2.678,-17.064],[-3.076,3.096],[0,0],[7.729,22.043]],"v":[[39.705,59.691],[7.239,-93.564],[-26.867,-103.981],[-33.451,-87.558],[11.785,112.186]],"c":false}]},{"t":40,"s":[{"i":[[0.605,2.359],[7.727,29.998],[8.683,-10.722],[-2.086,-8.159],[0,0]],"o":[[-7.42,-28.87],[-4.394,-16.705],[-2.747,3.392],[0,0],[0.547,2.933]],"v":[[39.186,57.705],[0.616,-92.483],[-34.371,-99.389],[-38.555,-82.695],[10.429,106.432]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Hand","parent":1,"sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":1,"s":[0]},{"i":{"x":[0.8],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":11,"s":[-10]},{"i":{"x":[0.4],"y":[1]},"o":{"x":[0.2],"y":[0]},"t":28,"s":[16]},{"t":40,"s":[0]}]},"p":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[30.844,100.173,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.8,"y":1},"o":{"x":0.7,"y":0},"t":9,"s":[17.036,149.143,0],"to":[38.218,3.829,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.2,"y":0},"t":26,"s":[63.112,93.173,0],"to":[0,0,0],"ti":[0,0,0]},{"t":38,"s":[30.844,100.173,0]}]},"a":{"a":0,"k":[32,12,0]},"s":{"a":1,"k":[{"i":{"x":[0.3,0.3,0.3],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.8,0.8,0.8],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.7],"y":[0,0,0]},"t":8,"s":[106,106,100]},{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":22,"s":[90,90,100]},{"t":35,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.3,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[{"i":[[-28.22,-8.972],[-20.962,-18.866],[-4.581,-3.968],[8.939,98.435],[0.323,1.294],[-5.597,-21.364],[0,0],[1.962,6.846],[0,0],[9.308,-1.939],[-3.244,-12.705],[0,0],[3.439,0.162],[0.624,2.302],[0,0],[-6.661,-23.932],[-2.106,-8.246],[3.441,0.162],[0.605,2.359],[0,0],[1.516,1.216],[35.915,-8.69]],"o":[[46.684,14.842],[3.839,3.455],[72.554,70.081],[-3.475,-29.276],[-6.675,-26.743],[0,0],[1.59,6.948],[0.299,-0.331],[-3.282,-12.736],[-9.763,2.034],[0,0],[0.93,3.418],[-2.341,-0.075],[0,0],[-5.77,-22.212],[2.415,8.677],[0.939,3.42],[-2.26,-0.072],[-7.42,-28.87],[0.547,2.933],[-8.12,-6.512],[-25.691,6.216]],"v":[[-118.508,41.093],[-47.28,79.322],[-34.89,90.319],[134.13,10.982],[105.637,-105.675],[72.796,-101.155],[84.058,-50.775],[72.794,-48.016],[58.772,-106.565],[37.853,-121.497],[26.211,-100.284],[38.18,-44.804],[31.924,-38.038],[26.917,-42.163],[11.766,-108.849],[-18.801,-101.502],[-4.243,-43.979],[-10.496,-37.204],[-15.4,-41.212],[-44.157,7.515],[-47.543,7.753],[-108.75,-5.575]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":9,"s":[{"i":[[-23.77,-17.66],[-43.188,-33.545],[-4.581,-3.968],[8.939,98.435],[0.614,1.184],[-8.389,-20.429],[0,0],[2.686,6.596],[0,0],[9.186,-0.97],[-4.905,-12.16],[0,0],[3.437,-0.209],[0.867,2.221],[0,0],[-9.786,-22.833],[-2.98,-7.972],[3.438,-0.209],[0.856,2.28],[0,0],[0.791,1.775],[35.619,9.832]],"o":[[43.243,32.127],[4.079,3.168],[95.57,64.254],[-0.384,-56.134],[-10.653,-20.524],[0,0],[2.327,6.737],[0.262,-0.362],[-4.947,-12.186],[-7.524,0.794],[0,0],[1.291,3.298],[-2.336,0.177],[0,0],[-8.674,-21.247],[3.548,8.279],[1.301,3.299],[-2.254,0.171],[-10.479,-27.906],[0.547,2.933],[-3.138,-7.041],[-30.584,-8.443]],"v":[[-120.346,-9.153],[-39.945,78.41],[-28.131,87.155],[137.912,-11.358],[111.776,-104.837],[73.219,-106.443],[93.283,-69.967],[82.381,-66.013],[60.878,-105.005],[34.71,-119.28],[24.104,-101.097],[48.312,-59.101],[42.819,-51.701],[37.398,-55.264],[13.981,-99.58],[-24.283,-94.772],[-3.042,-56.966],[-8.531,-49.558],[-13.837,-53.016],[-48.492,-12.13],[-45.921,-4.66],[-93.92,-44.037]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.167,"y":0.167},"t":29,"s":[{"i":[[-32.869,-11.641],[-20.962,-18.866],[-4.581,-3.968],[9.619,92.92],[0.276,1.305],[-4.817,-21.553],[0,0],[1.962,6.846],[0,0],[9.372,-1.6],[-2.78,-12.814],[0,0],[3.439,0.162],[0.624,2.302],[0,0],[-5.038,-20.346],[-2.106,-8.246],[3.441,0.162],[0.605,2.359],[0,0],[1.163,1.556],[24.029,-1.508]],"o":[[52.559,18.615],[3.839,3.455],[72.554,70.081],[-4.172,-36.454],[-5.699,-26.968],[0,0],[1.59,6.948],[0.299,-0.332],[-2.817,-12.847],[-9.83,1.678],[0,0],[0.93,3.418],[-2.341,-0.075],[0,0],[-4.959,-22.407],[2.165,8.743],[0.939,3.42],[-2.26,-0.072],[-7.42,-28.87],[0.547,2.933],[-8.857,-11.85],[-26.38,1.655]],"v":[[-115.186,28.411],[-43.694,74.715],[-31.304,85.712],[138.713,7.179],[109.645,-97.79],[75.839,-98.75],[86.841,-54.387],[75.577,-51.628],[61.903,-103.05],[41.54,-118.733],[29.135,-97.957],[40.721,-46.241],[34.465,-39.475],[29.458,-43.6],[17.129,-104.937],[-17.81,-106.641],[-4.243,-43.979],[-10.496,-37.204],[-15.4,-41.212],[-44.157,7.515],[-46.25,4.519],[-100.598,-16.494]],"c":true}]},{"t":40,"s":[{"i":[[-28.22,-8.972],[-20.962,-18.866],[-4.581,-3.968],[8.939,98.435],[0.323,1.294],[-5.597,-21.364],[0,0],[1.962,6.846],[0,0],[9.308,-1.939],[-3.244,-12.705],[0,0],[3.439,0.162],[0.624,2.302],[0,0],[-6.661,-23.932],[-2.106,-8.246],[3.441,0.162],[0.605,2.359],[0,0],[1.516,1.216],[35.915,-8.69]],"o":[[46.684,14.842],[3.839,3.455],[72.554,70.081],[-3.475,-29.276],[-6.675,-26.743],[0,0],[1.59,6.948],[0.299,-0.331],[-3.282,-12.736],[-9.763,2.034],[0,0],[0.93,3.418],[-2.341,-0.075],[0,0],[-5.77,-22.212],[2.415,8.677],[0.939,3.42],[-2.26,-0.072],[-7.42,-28.87],[0.547,2.933],[-8.12,-6.512],[-25.691,6.216]],"v":[[-118.508,41.093],[-47.28,79.322],[-34.89,90.319],[134.13,10.982],[105.637,-105.675],[72.796,-101.155],[84.058,-50.775],[72.794,-48.016],[58.772,-106.565],[37.853,-121.497],[26.211,-100.284],[38.18,-44.804],[31.924,-38.038],[26.917,-42.163],[11.766,-108.849],[-18.801,-101.502],[-4.243,-43.979],[-10.496,-37.204],[-15.4,-41.212],[-44.157,7.515],[-47.543,7.753],[-108.75,-5.575]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0}]} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/webview_app_ext.js b/TMessagesProj/src/main/res/raw/webview_app_ext.js index 1f156218f91..b6cbf0f4f2d 100644 --- a/TMessagesProj/src/main/res/raw/webview_app_ext.js +++ b/TMessagesProj/src/main/res/raw/webview_app_ext.js @@ -31,6 +31,8 @@ if (!window.__tg__webview_set) { document.addEventListener('touchstart', e => { whiletouchstart = false; }, false); + const atLeft = e => !e || e == document || e.scrollLeft <= 0 && atLeft(e.parentNode); + const atTop = e => !e || e == document || e.scrollTop <= 0 && atTop(e.parentNode); document.addEventListener('touchmove', e => { whiletouchstart = false; whiletouchmove = true; @@ -38,10 +40,10 @@ if (!window.__tg__webview_set) { setTimeout(() => { if (awaitingResponse) { if (window.TelegramWebviewProxy) { - const allowScrollX = !prevented && (!window.visualViewport || window.visualViewport.offsetLeft == 0) && !mutatedWhileTouch; - const allowScrollY = !prevented && (!window.visualViewport || window.visualViewport.offsetTop == 0) && !mutatedWhileTouch; + const allowScrollX = !prevented && atLeft(e.target) && (!window.visualViewport || window.visualViewport.offsetLeft == 0) && !mutatedWhileTouch; + const allowScrollY = !prevented && atTop(e.target) && (!window.visualViewport || window.visualViewport.offsetTop == 0) && !mutatedWhileTouch; if (DEBUG) { - console.log('tgbrowser allowScroll sent after "touchmove": x=' + allowScrollX + ' y=' + allowScrollY, { prevented, mutatedWhileTouch }); + console.log('tgbrowser allowScroll sent after "touchmove": x=' + allowScrollX + ' y=' + allowScrollY, { e, prevented, mutatedWhileTouch }); } window.TelegramWebviewProxy.postEvent('web_app_allow_scroll', JSON.stringify([ allowScrollX, allowScrollY ])); } @@ -65,7 +67,7 @@ if (!window.__tg__webview_set) { if (awaitingResponse) { if (window.TelegramWebviewProxy) { if (DEBUG) { - console.log('tgbrowser allowScroll sent after "scroll": x=' + allowScrollX + ' y=' + allowScrollY, { prevented, mutatedWhileTouch, scrollLeft: e.target.scrollLeft, scrollTop: e.target.scrollTop }); + console.log('tgbrowser allowScroll sent after "scroll": x=' + allowScrollX + ' y=' + allowScrollY, { e, prevented, mutatedWhileTouch, scrollLeft: e.target.scrollLeft, scrollTop: e.target.scrollTop }); } window.TelegramWebviewProxy.postEvent('web_app_allow_scroll', JSON.stringify([allowScrollX, allowScrollY])); } diff --git a/TMessagesProj/src/main/res/values/strings.xml b/TMessagesProj/src/main/res/values/strings.xml index 38ecf5d97d8..040b0b92b71 100644 --- a/TMessagesProj/src/main/res/values/strings.xml +++ b/TMessagesProj/src/main/res/values/strings.xml @@ -116,6 +116,7 @@ Transfer **%1$s** to the %2$s bot for **%3$s**? Total Invalid invoice + Already subscribed! INVOICE TEST INVOICE PAY %1$s @@ -862,6 +863,7 @@ un1 edited this message: un1 edited caption: un1 edited media: + un1 edited media: un1 edited media and caption: Original message Original caption @@ -1103,10 +1105,17 @@ 50% of the revenue from Telegram Ads goes to the owner of the channel where they are displayed. Can Be Removed You can turn off ads by subscribing to **Telegram Premium**, and Level %1$s channels can remove them for their subscribers. + You can turn off ads by subscribing to **Telegram Premium**. Can I Launch an Ad? + Can I Launch an Ad? Anyone can create an ad to display in this channel — with minimal budgets. Check out the **Telegram Ad Platform** for details. %1$s **Learn More >** Understood + Respect Your Privacy + Ads on Telegram do not use your personal information and are based on the bot in which you see them. + Help the Bot Creator + 50% of the revenue from Telegram Ads goes to the owner of the bot where they are displayed. + Anyone can create an ad to display in this bot — with minimal budgets. Check out the **Telegram Ad Platform** for details. %1$s https://ads.telegram.org Reference %1$s is playing a game... @@ -1130,7 +1139,7 @@ Location File No messages here yet... - No scheduled messages here yet... + No scheduled messages yet... From From %1$s from: @@ -1169,6 +1178,11 @@ Getting Link Info... OPEN IN... Open in... + Minimize + Save in %dp + ・cached + Speed + Loop Send as file Send as files Open Link @@ -1286,6 +1300,7 @@ Edit Message Edit Caption Tap to edit media + Tap to add media Edit this photo Edit this video Replace photo @@ -1735,6 +1750,7 @@ Are you sure you want to revoke the link **%1$s**?\n\nThe channel \"**%2$s**\" will become private. Copy Link Copy E-Mail + Copy Hashtag Copy Number Share Link Share Links @@ -3014,7 +3030,21 @@ Proximity alert cancelled Quality + Video Quality Auto + Original + Ultra High + Ultra High + High + Medium + Low + Low + Low + Low + 4K + 2K + HD + HD Source Speed Slow @@ -3420,6 +3450,7 @@ Always Allow Never Allow These users will or will not be able to add you to groups and channels regardless of the settings above. + You can add users or entire groups as exceptions that will override the settings above. Change who can add you to groups and channels. Sorry, you can\'t add this user to groups because of user\'s privacy settings. Sorry, you can\'t add this user to channels because of user\'s privacy settings. @@ -4035,6 +4066,7 @@ Live Stream Manage Live Streams Join + Notify Me %1$s member talking %1$s members talking speaking @@ -5971,6 +6003,8 @@ Edit Intro Edit Commands Balance + Toncoin + Stars Change Bot Settings Use @BotFather to manage this bot. Set public link @@ -7079,10 +7113,16 @@ Hide the time when you read messages from people who can’t see your last seen. If you turn this on, their read time will also be hidden from you.\nThis setting does not affect group chats. read show when - read today at %s - read yesterday at %s + read at %s + yesterday at %s read %1$s at %2$s read time unknown + edited at %s + edited yesterday at %s + edited %1$s at %2$s + original at %s + original yesterday at %s + original %1$s at %2$s Subscribe to Telegram Premium If you subscribe to Premium, you will see other users’ last seen and read time even if you hid yours from them (unless they specifically restricted it). Subscribed to Telegram Premium @@ -7381,14 +7421,14 @@ Telegram shares %1$d%% of the revenue from ads displayed in your channel. **Learn more >** Available balance You can collect your reward using Fragment, a third-party platform used by advertisers to pay for ads. **Learn more >** + https://telegram.org/tos/content-creator-rewards + In the coming weeks you will be able to collect your reward using Fragment, a third-party platform used by advertisers to pay ads. **Learn more >** Stars from your total balance become available for spending on ads and rewards 21 days after they are earned. https://telegram.org/tos/stars/ TON from your total balance become available for spending on ads and rewards 3 days after they are earned. https://telegram.org/tos/content-creator-rewards Stars and TON from your total balance become available for spending on ads and rewards 21 and 3 days respectively after they are earned. https://telegram.org/tos/content-creator-rewards - https://telegram.org/tos/content-creator-rewards - In the coming weeks you will be able to collect your reward using Fragment, a third-party platform used by advertisers to pay ads. **Learn more >** Transaction history Show %d more transaction Show %d more transactions @@ -7465,6 +7505,9 @@ 🎂 %2$s (%1$d years old) Date of Birth Birthday today + Gifts + Who can display gifts on my profile + Choose whether gifts from specific senders need your approval before they’re visible to others on your profile. Date of Birth Who can see my birthday? You can restrict who can see you birthday with granular precision. @@ -7488,7 +7531,20 @@ User Types Premium Users all Telegram Premium subscribers + Mini Apps & Bots + all bots Premium & %s + Mini Apps + Mini Apps & %s + Mini Apps + Except Mini Apps + Except Mini Apps (-%1$d) + Mini Apps + Mini Apps & %s + Contacts & Mini Apps + Contacts & Mini Apps (+%1$d) + Contacts & Mini Apps (-%1$d) + Contacts & Mini Apps (-%1$d, +%2$d) bot manages this chat bot stopped Start @@ -7654,25 +7710,39 @@ Stars Top-Up Bot In-App Purchase Ads + Paid Broadcast + %d Message + %d Messages Purchase from Fragment Withdraw to Fragment Stars Acquired! **%d Stars** added to your balance. Purchase Completed! You acquired "**%2$s**" in **%3$s** for **%1$d Stars**. + Subscription purchased! + You subscribed to "**%2$s**" in **%3$s** for **%1$d Stars**/month. Media Unlocked **%1$d Star** transferred to **%2$s**. **%1$d Stars** transferred to **%2$s**. Confirm Your Purchase - Do you want to buy "**%2$s**" in **%3$s** for **%1$d star**? - Do you want to buy "**%2$s**" in **%3$s** for **%1$d stars**? + Do you want to buy **%2$s** in **%3$s** for **%1$d** star? + Do you want to buy **%2$s** in **%3$s** for **%1$d** stars? Confirm and Pay ⭐️ %d Star Confirm and Pay ⭐️ %d Stars + By purchasing you agree to the **Terms of Service**. + Confirm Your Subscription + Do you want to subscribe to **%2$s** in **%3$s** for **%1$d** star per month? + Do you want to subscribe to **%2$s** in **%3$s** for **%1$d** stars per month? + Subscribe for ⭐️ %d / month + By subscribing you agree to the **Terms of Service**. %d Star Needed %d Stars Needed Buy **Stars** and use them on **%s** and other miniapps. + Buy **Stars** and use them on **%s** and businesses. Buy **Stars** and subscribe to **%s** and other channels. Buy **Stars** to keep your subscription for **%s**. + Buy **Stars** to keep your subscription for **%s**. + Buy **Stars** to keep your subscription for **%s**. Buy **Stars** to send paid reactions to **%s** and other channels. Buy **Stars** to send gifts to **%s** and other contacts. Buy **Stars** to keep your channel subscriptions. @@ -7705,6 +7775,9 @@ Transaction ID Transaction ID copied to clipboard Date + Messages + %d + %d TON Transaction Date View in Blockchain Explorer Review the **Terms of Service** for Stars. @@ -7750,6 +7823,13 @@ %d Story found %d Stories found View stories with %s + %1$d story in **%2$s** + %1$d stories in **%2$s** + %d message found + %d messages found + %1$d message in **%2$s** + %1$d messages in **%2$s** + View posts with %s Limit Reached You can’t add more than %d links to a story. Next withdrawal will be available in **%s**. @@ -7817,6 +7897,7 @@ Oops... Failed to load page. Failed to load **%s**. + Failed to load **%s**. Downloading... Downloading %s... Download file @@ -7872,6 +7953,8 @@ Examples %d user %d users + %d user + %d users Apps history Do you want to remove %s from your apps usage history? Bookmark @@ -7976,15 +8059,23 @@ expires on %s expired on %s cancelled + cancelled + cancelled expired Subscription ⭐️%1$d/month Subscription + Bot + Subscription + Business + Subscription Subscribed Renews Expires Expired You have cancelled your subscription. + Bot has cancelled your subscription. + Business has cancelled your subscription. Renew Subscription Subscribe Again Cancel Subscription @@ -7992,6 +8083,8 @@ Your subscription expired on %s. Join Channel You left channel, but you can still get back until %s. + Refulfill Subscription + You can still refulfill subscription back until %s. ⭐️%1$d Star needed for %2$s ⭐️%1$d Stars needed for %2$s Insufficient funds to cover your subscription. @@ -8039,6 +8132,8 @@ %d expired subscriptions Subscription cancelled You will still have access to the channel until **%s**. + You will still have access to **%2$s** until **%1$s**. + You will still have access to **%2$s** until **%1$s**. Subscription renewed You renewed your subscription to **%s**. Star Reactions were disabled on **%s**. @@ -8071,7 +8166,9 @@ You received a gift! Display this gift on your page or convert it to %d Star. Display this gift on your page or convert it to %d Stars. + You can display this gift on your page. You kept this gift on your page. + This gift is now visible on your page. You converted this gift to %d Star. You converted this gift to %d Stars. **%2$s** can display this gift on their page or convert it to %1$d Star. @@ -8095,23 +8192,31 @@ Gift Received Gift Premium - You can keep this gift in your Profile or convert it to **%d** Star. - You can keep this gift in your Profile or convert it to **%d** Stars. + You can keep this gift in your Profile or convert it to **%1$d** Star within **%2$s**. + You can keep this gift in your Profile or convert it to **%1$d** Stars within **%2$s**. + You can keep this %d Star gift in your Profile. + You can keep this %d Stars gift in your Profile. + You can remove this gift from your Profile. + You can keep this gift in your Profile. %2$s can keep this gift in profile or convert it to **%1$d** Star. %2$s can keep this gift in profile or convert it to **%1$d** Stars. You converted this gift to **%d** Star. You converted this gift to **%d** Stars. - You kept this gift in your Profile. %2$s converted this gift to **%d** Star. %2$s converted this gift to **%d** Stars. %1$s kept this gift in your Profile. - Only you can see the senderʼs name. + Only you can see the senderʼs name and message. This gift is visible to visitors of your page. This gift is visible to visitors of your page. **View >** This gift is hidden. Only you can see it. **More about Stars >** From To + Visibility + Visible on your page + hide + Not visible on your page + show Availability %1$d of %2$s left %1$d of %2$s left @@ -8179,4 +8284,106 @@ send a gift sell for %d Star sell for %d Stars + Add Photo or Video + Add File + Add Music + To Photo or Video + To File + To Music + Public posts + Public Posts + Show more \> + Which apps are included here? **Learn \>** + Top Mini Apps + This catalogue ranks mini apps based on their daily revenue, measured in Stars. To be listed, developers must set their main mini apps in @botfather (as described [here](https://core.telegram.org/bots/webapps#launching-the-main-mini-app)), have over **1,000** daily users, and earn a daily revenue above **1,000** Stars, based on the weekly average. + Understood + appx. %s + Improving Video... + The video will be published after it\'s optimized for the best viewing experience. + The video will be published once converted and optimized + The video will be published once converted and optimized. + Processing video may take a few minutes. + what\'s this? + Stars + Toncoin + Ad impressions + Ad revenue + Proceeds overview + Balance available to withdraw + Proceeds since last withdrawal + Total lifetime proceeds + Available balance + Transactions + Telegram shares %1$d%% of the revenue from ads displayed in your bot. **Learn more >** + TON from your total balance become available for spending on ads and rewards 3 days after they are earned. + https://telegram.org/tos/content-creator-rewards + You can collect your reward using Fragment, a third-party platform used by advertisers to pay for ads. **Learn more >** + https://telegram.org/tos/content-creator-rewards + In the coming weeks you will be able to collect your reward using Fragment, a third-party platform used by advertisers to pay ads. **Learn more >** + Earn From Your Bot + Telegram Ads + Telegram can display ads in your bot. + 50:50 revenue split + You can receive 50% of the ad revenue in TON. + Flexible withdrawals + You can withdraw your TON any time. + What is 💎 TON? + **TON** is a blockchain platform and cryptocurrency that Telegram uses for its high speed and low commisions on transactions. + https://telegram.org/privacy + Use %s + searches posts from all channels + Use %s + searches only posts from your channel + Video published. + View + Swipe sideways to change speed + Wait! + This video hasn\'t been converted and optimized yet. If you send it now, the viewers of the video may experience slow download speed. + Send Anyway + Confirm + Do you want to set this emoji status suggested by **%1$s**? + Do you want to set this emoji status suggested by **%1$s** for **%2$s**? + %d minute + %d minutes + %d hour + %d hours + %d day + %d days + **%1$s** requests access to set your **emoji status**. You will be able to revoke this access in the profile page of **%2$s**. + **%1$s** requests access to your location. You will be able to revoke this access in the profile page of **%2$s**. + Allow + Decline + Allow + Decline + Settings + Allow access to + Geolocation + Emoji Status + Biometry + **%s** can now have access to your location + **%s** can\'t have access to your location since you restricted Telegram from accessing it. + Change in Settings \> + **%s** can now set your emoji status anytime. + Your emoji status is updated. + Back + Close + Downloads + Download Document + **%s** suggests you download the following file: + Download + Share Message + Message preview + %s mini app suggests you to send this message to a chat you select. + Share with... + Share to… + Sent message to **%s** + Sent message to %d chat + Sent message to %d chats + You will be notified when the live stream starts. + You will not be notified when the live stream starts. + Open + Downloading... + File saved to Downloads + Open + Cancel \ No newline at end of file diff --git a/TMessagesProj_AppHockeyApp/src/main/java/org/telegram/messenger/ApplicationLoaderImpl.java b/TMessagesProj_AppHockeyApp/src/main/java/org/telegram/messenger/ApplicationLoaderImpl.java index cd31b8a7958..250dbf79822 100644 --- a/TMessagesProj_AppHockeyApp/src/main/java/org/telegram/messenger/ApplicationLoaderImpl.java +++ b/TMessagesProj_AppHockeyApp/src/main/java/org/telegram/messenger/ApplicationLoaderImpl.java @@ -61,7 +61,13 @@ protected void startAppCenterInternal(Activity context) { props.set("hardware", Build.HARDWARE); props.set("user", Build.USER); AppCenter.setCustomProperties(props); - AppCenter.setUserId("uid=" + UserConfig.getInstance(UserConfig.selectedAccount).clientUserId); + String userId = "uid=" + UserConfig.getInstance(UserConfig.selectedAccount).clientUserId; + if (UserConfig.getInstance(UserConfig.selectedAccount).getCurrentUser() != null) { + final String username = UserObject.getPublicUsername(UserConfig.getInstance(UserConfig.selectedAccount).getCurrentUser()); + if (!TextUtils.isEmpty(username)) + userId += " @" + username; + } + AppCenter.setUserId(userId); } } catch (Throwable e) { FileLog.e(e); diff --git a/TMessagesProj_AppStandalone/src/main/java/org/telegram/messenger/ApplicationLoaderImpl.java b/TMessagesProj_AppStandalone/src/main/java/org/telegram/messenger/ApplicationLoaderImpl.java index 39513d92224..55975728993 100644 --- a/TMessagesProj_AppStandalone/src/main/java/org/telegram/messenger/ApplicationLoaderImpl.java +++ b/TMessagesProj_AppStandalone/src/main/java/org/telegram/messenger/ApplicationLoaderImpl.java @@ -25,7 +25,9 @@ import org.telegram.ui.Components.BulletinFactory; import org.telegram.ui.Components.SpannableStringLight; import org.telegram.ui.Components.UpdateAppAlertDialog; +import org.telegram.ui.Components.UpdateButton; import org.telegram.ui.Components.UpdateLayout; +import org.telegram.ui.IUpdateButton; import org.telegram.ui.IUpdateLayout; import org.telegram.ui.LaunchActivity; import org.telegram.ui.SMSStatsActivity; @@ -114,6 +116,11 @@ public IUpdateLayout takeUpdateLayout(Activity activity, ViewGroup sideMenu, Vie return new UpdateLayout(activity, sideMenu, sideMenuContainer); } + @Override + public IUpdateButton takeUpdateButton(Context context) { + return new UpdateButton(context); + } + @Override public TLRPC.Update parseTLUpdate(int constructor) { if (constructor == TL_smsjobs.TL_updateSmsJob.constructor) { diff --git a/TMessagesProj_AppStandalone/src/main/java/org/telegram/ui/Components/UpdateButton.java b/TMessagesProj_AppStandalone/src/main/java/org/telegram/ui/Components/UpdateButton.java new file mode 100644 index 00000000000..b62108b5ce5 --- /dev/null +++ b/TMessagesProj_AppStandalone/src/main/java/org/telegram/ui/Components/UpdateButton.java @@ -0,0 +1,181 @@ +package org.telegram.ui.Components; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Shader; +import android.os.Build; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.TextView; + +import androidx.annotation.Keep; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.Utilities; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.IUpdateButton; + +import java.io.File; + +public class UpdateButton extends IUpdateButton { + + private AnimatorSet animator; + private RadialProgress2 icon; + private TextView textView; + + @Keep + public UpdateButton(Context context) { + super(context); + + setWillNotDraw(false); + setVisibility(View.INVISIBLE); + setTranslationY(dp(48)); + if (Build.VERSION.SDK_INT >= 21) { + setBackground(Theme.getSelectorDrawable(0x40ffffff, false)); + } + setOnClickListener(v -> { + if (!SharedConfig.isAppUpdateAvailable()) return; + Activity activity = AndroidUtilities.findActivity(getContext()); + if (activity == null) return; + AndroidUtilities.openForView(SharedConfig.pendingAppUpdate.document, true, activity); + }); + + icon = new RadialProgress2(this); + icon.setColors(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff); + icon.setCircleRadius(dp(11)); + icon.setAsMini(); + icon.setIcon(MediaActionDrawable.ICON_UPDATE, true, false); + + textView = new TextView(context); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); + textView.setTypeface(AndroidUtilities.bold()); + textView.setText(LocaleController.getString(org.telegram.messenger.R.string.AppUpdateNow).toUpperCase()); + textView.setTextColor(0xffffffff); + textView.setPadding(dp(30), 0, 0, 0); + addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 0, 0, 0, 0)); + } + + + private Paint paint = new Paint(); + private Matrix matrix = new Matrix(); + private LinearGradient updateGradient; + private int lastGradientWidth; + + @Override + public void draw(Canvas canvas) { + if (updateGradient != null) { + paint.setColor(0xffffffff); + paint.setShader(updateGradient); + updateGradient.setLocalMatrix(matrix); + canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint); + icon.setBackgroundGradientDrawable(updateGradient); + icon.draw(canvas); + } + super.draw(canvas); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int width = MeasureSpec.getSize(widthMeasureSpec); + if (lastGradientWidth != width) { + updateGradient = new LinearGradient(0, 0, width, 0, new int[]{0xff69BF72, 0xff53B3AD}, new float[]{0.0f, 1.0f}, Shader.TileMode.CLAMP); + lastGradientWidth = width; + } + int x = (getMeasuredWidth() - textView.getMeasuredWidth()) / 2; + icon.setProgressRect(x, dp(13), x + dp(22), dp(13 + 22)); + } + + + private Utilities.Callback onTranslationUpdate; + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + if (onTranslationUpdate != null) { + onTranslationUpdate.run(translationY); + } + } + + @Keep + public void onTranslationUpdate(Utilities.Callback onTranslationUpdate) { + this.onTranslationUpdate = onTranslationUpdate; + } + + @Keep + public void update(boolean animated) { + final boolean show; + if (SharedConfig.isAppUpdateAvailable()) { + final String fileName = FileLoader.getAttachFileName(SharedConfig.pendingAppUpdate.document); + final File path = FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(SharedConfig.pendingAppUpdate.document, true); + show = path.exists(); + } else { + show = false; + } + if (show) { + if (getTag() != null) { + return; + } + if (animator != null) { + animator.cancel(); + } + setVisibility(View.VISIBLE); + setTag(1); + if (animated) { + animator = new AnimatorSet(); + animator.setDuration(180); + animator.setInterpolator(CubicBezierInterpolator.EASE_OUT); + animator.playTogether(ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, 0)); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + animator = null; + } + }); + animator.start(); + } else { + setTranslationY(0); + } + } else { + if (getTag() == null) { + return; + } + setTag(null); + if (animated) { + animator = new AnimatorSet(); + animator.setDuration(180); + animator.setInterpolator(CubicBezierInterpolator.EASE_OUT); + animator.playTogether(ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, dp(48))); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (getTag() == null) { + setVisibility(View.INVISIBLE); + } + animator = null; + } + }); + animator.start(); + } else { + setTranslationY(dp(48)); + setVisibility(View.INVISIBLE); + } + } + } + +} diff --git a/gradle.properties b/gradle.properties index dc6f5e00dff..2655cb26f90 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=5335 -APP_VERSION_NAME=11.2.3 +APP_VERSION_CODE=5469 +APP_VERSION_NAME=11.4.2 APP_PACKAGE=org.telegram.messenger IS_PRIVATE=false RELEASE_KEY_PASSWORD=android