diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml index 00aec3fc4..495dce2c6 100644 --- a/.github/workflows/alpha.yml +++ b/.github/workflows/alpha.yml @@ -18,7 +18,7 @@ jobs: - name: Save artifact uses: actions/upload-artifact@master with: - name: PTelegramAlpha-v1482.apk + name: PTelegramAlpha-v1491.apk path: TMessagesProj/build/outputs/apk/afat/alpha/app.apk - name: Create cleanup request run: touch delete_request diff --git a/.github/workflows/beta.yml b/.github/workflows/beta.yml index caa80c6e7..f5e4b0546 100644 --- a/.github/workflows/beta.yml +++ b/.github/workflows/beta.yml @@ -18,7 +18,7 @@ jobs: - name: Save artifact uses: actions/upload-artifact@master with: - name: PTelegramBeta-v1482.apk + name: PTelegramBeta-v1491.apk path: TMessagesProj/build/outputs/apk/afat/debug/app.apk - name: Create cleanup request run: touch delete_request diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index df1401348..ab270a5d3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,8 +13,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token with: - tag_name: 3.7.4 - release_name: 3.7.4 + tag_name: 3.7.5 + release_name: 3.7.5 body: | Bug fixes. draft: false diff --git a/TMessagesProj/build.gradle b/TMessagesProj/build.gradle index 396ce4972..5d5238d6f 100644 --- a/TMessagesProj/build.gradle +++ b/TMessagesProj/build.gradle @@ -239,7 +239,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 f2b998fcf..df9bd34ac 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 cdb8e10b7..34cbe2e47 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 3e86a329d..bcd7507aa 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 4bd6439a4..85977b7a3 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 591f2207b..2cd195640 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 6b3846183..cacce3eed 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 000000000..0d5bca318 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 69efdf612..f876f392f 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 9f0c6d211..7390fef5d 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 0d8886b8b..0bb77cf73 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 4d4a94856..21bb8cd80 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 c43fffeaa..1f29b8e7e 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 a4816891d..43f5ae9eb 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 5dde31d83..8a6a17e9d 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 000000000..564a45c40 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 fb86241f9..655f39341 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 f35501bca..e5f72f9a5 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 a6d9ffcaa..ab2abbee5 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 dd912321c..636f2e018 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 000000000..22b474926 --- /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 000000000..290e6ace3 --- /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 000000000..e551ad650 --- /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 000000000..e8f070577 --- /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 000000000..b9037f23d --- /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 000000000..cc291a4ab --- /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 000000000..43df60391 --- /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 002a567a9..2d0c3f644 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 00c5ac2ef..436386042 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 81563020c..c618161f3 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 f03fd350b..ddc28b019 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 000000000..315d475fb 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 00a5203be..102a354bd 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 fb56c176c..0dbc4be5f 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 de3c1db95..ad2af0d36 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 e54555cb9..6f7eaf464 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 f8e543074..ea06e7b51 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 a2e5997b6..9445f6416 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 d982f1067..f8ba4771a 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 000000000..bae7c5c0a 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 230a28697..a55236857 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 3187be8df..e0dc487b5 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 bd0383e80..4c858b539 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 c3bc4ff65..ffe2f8e5b 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 74862f229..b1dd44daf 100644 --- a/TMessagesProj/jni/tgnet/ConnectionsManager.cpp +++ b/TMessagesProj/jni/tgnet/ConnectionsManager.cpp @@ -651,6 +651,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; @@ -1234,6 +1238,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 { @@ -1266,7 +1272,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) { @@ -1830,9 +1836,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(); @@ -2608,12 +2616,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 893fd96cb..1937118a3 100644 --- a/TMessagesProj/jni/tgnet/ConnectionsManager.h +++ b/TMessagesProj/jni/tgnet/ConnectionsManager.h @@ -46,6 +46,7 @@ public: int64_t getCurrentTimeMillis(); int64_t getCurrentTimeMonotonicMillis(); int32_t getCurrentTime(); + int32_t getCurrentPingTime(); uint32_t getCurrentDatacenterId(); bool isTestBackend(); int32_t getTimeDifference(); @@ -142,6 +143,7 @@ private: 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 @@ private: 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 c4be118df..70d717c89 100644 --- a/TMessagesProj/proguard-rules.pro +++ b/TMessagesProj/proguard-rules.pro @@ -31,6 +31,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 399f74e3a..e0cc9cd81 100644 --- a/TMessagesProj/src/main/AndroidManifest.xml +++ b/TMessagesProj/src/main/AndroidManifest.xml @@ -594,6 +594,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 class AdaptiveTrackSelection extends BaseTrackSelection { 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 class AdaptiveTrackSelection extends BaseTrackSelection { */ @SuppressWarnings("unused") protected boolean canSelectFormat(Format format, int trackBitrate, long effectiveBitrate) { - return trackBitrate <= effectiveBitrate; + return format.cached || trackBitrate <= effectiveBitrate; } /** @@ -586,19 +589,62 @@ public class AdaptiveTrackSelection extends BaseTrackSelection { * @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)) { - Format format = getFormat(i); - if (canSelectFormat(format, format.bitrate, effectiveBitrate)) { - return i; - } else { - lowestBitrateAllowedIndex = i; + 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 (format.cached) { + FileLog.d("debug_loading_player: determineIdealSelectedIndex: initial setup, choose cached format#" + i); + return 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 1e2c4a5bd..0c1103f1c 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 @@ import java.util.ArrayList; */ 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 7803f316e..970df0f33 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 @@ package com.google.android.exoplayer2.upstream; 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.base.Ascii; 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 final class DefaultBandwidthMeter implements BandwidthMeter, TransferList 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 final class DefaultBandwidthMeter implements BandwidthMeter, TransferList 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 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList } 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 3013fa140..3c3d165be 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java @@ -668,21 +668,31 @@ public class AndroidUtilities { } 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); - } - - @Override - public void onClick(@NonNull View view) { - if (onClick != null) { - onClick.run(); + if (onClick != null) { + spannableStringBuilder.setSpan(new ClickableSpan() { + @Override + public void updateDrawState(@NonNull TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + ds.setColor(color); } - } - }, index, index + len, 0); + + @Override + public void onClick(@NonNull View view) { + if (onClick != null) { + onClick.run(); + } + } + }, 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; } @@ -3204,8 +3214,36 @@ public class AndroidUtilities { 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("\\[(.+?)\\]\\((.+?)\\)"); } @@ -3222,6 +3260,9 @@ public class AndroidUtilities { 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 0f3c6e732..8e8a4c58d 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java @@ -48,6 +48,8 @@ import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.BaseFragment; 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; @@ -341,6 +343,7 @@ public class ApplicationLoader extends Application { } 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(); @@ -721,6 +724,10 @@ public class ApplicationLoader extends Application { 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 9b3eaf68d..e0d8c2573 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 class BillingController implements PurchasesUpdatedListener, BillingClien 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 ce547ec57..ac7b3db56 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/BirthdayController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/BirthdayController.java @@ -292,6 +292,15 @@ public class BirthdayController { } } + 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 000000000..15485ff14 --- /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 bacc6f5a5..70947cf36 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java @@ -90,6 +90,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; @@ -104,8 +105,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 { @@ -337,6 +339,7 @@ public class ContactsController extends BaseController { profilePhotoPrivacyRules = null; bioPrivacyRules = null; birthdayPrivacyRules = null; + giftsPrivacyRules = null; forwardsPrivacyRules = null; phonePrivacyRules = null; @@ -2704,6 +2707,9 @@ public class ContactsController extends BaseController { 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; @@ -2739,6 +2745,9 @@ public class ContactsController extends BaseController { 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; @@ -2803,6 +2812,8 @@ public class ContactsController extends BaseController { 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: @@ -2838,6 +2849,9 @@ public class ContactsController extends BaseController { 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 7be50cc80..98b2dc027 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.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; import java.util.ArrayList; +import java.util.HashSet; public class DialogObject { @@ -169,13 +170,106 @@ public class DialogObject { 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) { - return UserObject.getPublicUsername((TLRPC.User) dialog); + 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) { + 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 44985c1f9..56dac2155 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 class DownloadController extends BaseController implements NotificationCe } 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 class DownloadController extends BaseController implements NotificationCe } 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 8aab6ea5f..5926d0468 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 class FileLoadOperation { 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 @@ public class FileLoadOperation { 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 class FileLoadOperation { } } 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 class FileLoadOperation { 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 class FileLoadOperation { 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 class FileLoadOperation { 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 class FileLoadOperation { 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,64 +1341,68 @@ public class FileLoadOperation { 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); - } - } - 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 (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); - } - } - } + 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 (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 (cacheFilePreload != null) { + try { + if (!cacheFilePreload.delete()) { + cacheFilePreload.deleteOnExit(); + } + } catch (Exception e) { + FileLog.e(e); + } + } + } + } + private void cancelRequests(Runnable fullyCancelled) { FileLog.d("cancelRequests" + (fullyCancelled != null ? " with callback" : "")); if (requestInfos != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java index 4cd8c551e..f004af5d8 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 @@ public class FileLoader extends BaseController { } } + 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 08b809766..5fb684696 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 class FilePathDatabase { } 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 8863ea054..cc560f0f6 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 class FileStreamLoadOperation extends BaseDataSource implements FileLoadO 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 class FileStreamLoadOperation extends BaseDataSource implements FileLoadO } } } -// 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 class FileStreamLoadOperation extends BaseDataSource implements FileLoadO 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 class FileStreamLoadOperation extends BaseDataSource implements FileLoadO @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 class FileStreamLoadOperation extends BaseDataSource implements FileLoadO 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 04315b10f..446ea747f 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 class HashtagSearchController { 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 class HashtagSearchController { 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 class HashtagSearchController { } @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 class HashtagSearchController { 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 = hashtag; + search.lastHashtag = _query; + final String query = _query; - final String query = hashtag; - int limit = 30; + 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; + } + } + + 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 class HashtagSearchController { } 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 class HashtagSearchController { 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 class HashtagSearchController { 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 class HashtagSearchController { } } - 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 @@ public class HashtagSearchController { } 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 df690e530..1ff5b1a65 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 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg 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 0a577dcf2..fc5bd764a 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java @@ -2103,6 +2103,56 @@ public class LocaleController { 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 bfffef1cb..62420c6b4 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 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, } } } 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 d8fee6d0e..16db3785b 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; @@ -1285,6 +1286,32 @@ public class MediaDataController extends BaseController { }); } + 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); } @@ -1317,11 +1344,15 @@ public class MediaDataController extends BaseController { } 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); } @@ -1330,8 +1361,9 @@ public class MediaDataController extends BaseController { 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); } @@ -1342,6 +1374,8 @@ public class MediaDataController extends BaseController { getNotificationCenter().postNotificationName(NotificationCenter.groupStickersDidLoad, set.set.id, set); } }); + } else { + loadingStickerSetsKeys.remove(key); } }); }); @@ -1350,6 +1384,7 @@ public class MediaDataController extends BaseController { 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); } @@ -1358,8 +1393,9 @@ public class MediaDataController extends BaseController { 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); } @@ -1370,11 +1406,14 @@ public class MediaDataController extends BaseController { 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); } @@ -1393,6 +1432,8 @@ public class MediaDataController extends BaseController { getNotificationCenter().postNotificationName(NotificationCenter.groupStickersDidLoad, set.set.id, set); } }); + } else { + loadingStickerSetsKeys.remove(key); } return null; } @@ -1533,26 +1574,27 @@ public class MediaDataController extends BaseController { 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) { @@ -1562,7 +1604,7 @@ public class MediaDataController extends BaseController { } } } - loadingStickerSets.remove(inputStickerSet); + loadingStickerSets.remove(key); }); }); } @@ -3801,6 +3843,7 @@ public class MediaDataController extends BaseController { 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; } @@ -3936,7 +3979,7 @@ public class MediaDataController extends BaseController { if (messageObject.hasValidGroupId()) { messageObject.isPrimaryGroupMessage = true; } - messageObject.setQuery(finalQuery); + messageObject.setQuery(finalQuery, !isHashtag); messageObjects.add(messageObject); } } @@ -5468,6 +5511,7 @@ public class MediaDataController extends BaseController { 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); @@ -5507,10 +5551,18 @@ public class MediaDataController extends BaseController { 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; @@ -5519,6 +5571,9 @@ public class MediaDataController extends BaseController { 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); @@ -5527,9 +5582,15 @@ public class MediaDataController extends BaseController { } 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; } @@ -5618,9 +5679,9 @@ public class MediaDataController extends BaseController { 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)); @@ -5640,7 +5701,21 @@ public class MediaDataController extends BaseController { } } - 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) { @@ -5752,12 +5827,7 @@ public class MediaDataController extends BaseController { } 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 ---------------- @@ -7684,10 +7754,14 @@ public class MediaDataController extends BaseController { } 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 8077a925f..18e774033 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 @@ public class MessageCustomParamsHelper { 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 1f5ce41e5..715e608b9 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java @@ -198,6 +198,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; @@ -312,6 +313,7 @@ public class MessageObject { public CharSequence vCardData; public ArrayList highlightedWords; + public boolean messageTrimmedToHighlightCut = true; public CharSequence messageTrimmedToHighlight; public int parentWidth; @@ -478,6 +480,45 @@ public class MessageObject { 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); } @@ -2629,19 +2670,30 @@ public class MessageObject { 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); @@ -5871,6 +5923,9 @@ public class MessageObject { } public String getFileName() { + if (getDocument() != null) { + return getFileName(getDocument()); + } return getFileName(messageOwner); } @@ -6188,7 +6243,7 @@ public class MessageObject { 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); } @@ -6342,7 +6397,7 @@ public class MessageObject { // 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() { @@ -7896,7 +7951,7 @@ public class MessageObject { 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 { @@ -7910,10 +7965,6 @@ public class MessageObject { 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; } @@ -8164,6 +8215,7 @@ public class MessageObject { } 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; } @@ -8173,7 +8225,11 @@ public class MessageObject { } 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() { @@ -8184,10 +8240,6 @@ public class MessageObject { messageOwner.unread = false; } - public int getUnradFlags() { - return getUnreadFlags(messageOwner); - } - public static int getUnreadFlags(TLRPC.Message message) { int flags = 0; if (!message.unread) { @@ -8234,6 +8286,11 @@ public class MessageObject { } public long getSize() { + if (highestQuality != null) { + return highestQuality.document.size; + } else if (thumbQuality != null) { + return thumbQuality.document.size; + } return getMessageSize(messageOwner); } @@ -8366,6 +8423,7 @@ public class MessageObject { } public boolean canStreamVideo() { + if (hasVideoQualities()) return true; TLRPC.Document document = getDocument(); if (document == null || document instanceof TLRPC.TL_documentEncrypted) { return false; @@ -8470,7 +8528,7 @@ public class MessageObject { } 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() { @@ -8705,6 +8763,9 @@ public class MessageObject { if (emojiAnimatedSticker != null) { return emojiAnimatedSticker; } + if (hasVideoQualities() && highestQuality != null) { + return highestQuality.document; + } return getDocument(messageOwner); } @@ -9647,6 +9708,8 @@ public class MessageObject { return true; } else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaDocument) { return !isVoice() && !isSticker() && !isAnimatedSticker() && !isRoundVideo(); + } else if (isMediaEmpty()) { + return true; } return false; } @@ -9679,6 +9742,7 @@ public class MessageObject { } 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) { @@ -9994,37 +10058,42 @@ public class MessageObject { 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) { @@ -10032,7 +10101,7 @@ public class MessageObject { 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)); } @@ -10047,7 +10116,7 @@ public class MessageObject { 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)); } } @@ -10058,7 +10127,7 @@ public class MessageObject { 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++) { @@ -10093,10 +10162,13 @@ public class MessageObject { } } } - 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++) { @@ -10156,11 +10228,12 @@ public class MessageObject { 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; } } } @@ -11066,11 +11139,21 @@ public class MessageObject { } 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; @@ -11082,4 +11165,21 @@ public class MessageObject { 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 856167289..ade740db8 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java @@ -751,6 +751,7 @@ public class MessagesController extends BaseController implements NotificationCe public int stargiftsMessageLengthMax; public int stargiftsConvertPeriodMax; public boolean videoIgnoreAltDocuments; + public boolean disableBotFullscreenBlur; public int checkResetLangpack; public boolean folderTags; @@ -1019,7 +1020,6 @@ public class MessagesController extends BaseController implements NotificationCe public Integer posts_between; public long loadTime; public boolean loading; - public boolean faked; } private class SendAsPeersInfo { @@ -1608,6 +1608,7 @@ public class MessagesController extends BaseController implements NotificationCe 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); @@ -2436,7 +2437,7 @@ public class MessagesController extends BaseController implements NotificationCe private Runnable loadAppConfigRunnable = this::loadAppConfig; public void loadAppConfig() { - loadAppConfig(false); + loadAppConfig(true); } public void loadAppConfig(boolean force) { @@ -3793,6 +3794,17 @@ public class MessagesController extends BaseController implements NotificationCe } 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; @@ -4984,7 +4996,8 @@ public class MessagesController extends BaseController implements NotificationCe 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) { @@ -8384,18 +8397,26 @@ public class MessagesController extends BaseController implements NotificationCe } public void deleteMessages(ArrayList messages, ArrayList randoms, TLRPC.EncryptedChat encryptedChat, long dialogId, int topicId, boolean forAll, int mode) { - deleteMessages(messages, randoms, encryptedChat, dialogId, forAll, mode, false, 0, null, topicId, true, false); + deleteMessages(messages, randoms, encryptedChat, dialogId, forAll, mode, false, 0, null, topicId); } public void deleteMessages(ArrayList messages, ArrayList randoms, TLRPC.EncryptedChat encryptedChat, long dialogId, int topicId, boolean forAll, int mode, boolean cacheOnly) { - deleteMessages(messages, randoms, encryptedChat, dialogId, forAll, mode, cacheOnly, 0, null, topicId, true, false); + deleteMessages(messages, randoms, encryptedChat, dialogId, forAll, mode, cacheOnly, 0, null, topicId); } 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, true, false); + deleteMessages(messages, randoms, encryptedChat, dialogId, forAll, mode, cacheOnly, taskId, taskRequest, topicId, false, 0, true, false); + } + + 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) { + deleteMessages(messages, randoms, encryptedChat, dialogId, forAll, mode, cacheOnly, taskId, taskRequest, topicId, movedToScheduled, movedToScheduledMessageId, true, false); } 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 useQueue, boolean reset) { + deleteMessages(messages, randoms, encryptedChat, dialogId, forAll, mode, cacheOnly, taskId, taskRequest, topicId, false, 0, useQueue, reset); + } + + 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, boolean useQueue, boolean reset) { final boolean scheduled = mode == ChatActivity.MODE_SCHEDULED; final boolean quickReplies = mode == ChatActivity.MODE_QUICK_REPLIES; if ((messages == null || messages.isEmpty()) && taskId == 0) { @@ -8441,7 +8462,7 @@ public class MessagesController extends BaseController implements NotificationCe getMessagesStorage().markMessagesAsDeleted(dialogId, messages, useQueue, forAll, 0, topicId); getMessagesStorage().updateDialogsWithDeletedMessages(dialogId, channelId, messages, null, useQueue); } - 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; @@ -10922,6 +10943,14 @@ public class MessagesController extends BaseController implements NotificationCe }); } + 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; @@ -16935,6 +16964,7 @@ public class MessagesController extends BaseController implements NotificationCe LongSparseArray> deletedMessages = null; LongSparseArray> deletedQuickReplyMessages = null; LongSparseArray> scheduledDeletedMessages = null; + LongSparseArray> scheduledDeletedMessagesSent = null; LongSparseArray> groupSpeakingActions = null; LongSparseIntArray importingActions = null; LongSparseIntArray clearHistoryMessages = null; @@ -17284,12 +17314,24 @@ public class MessagesController extends BaseController implements NotificationCe scheduledDeletedMessages = new LongSparseArray<>(); } 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; @@ -18181,6 +18223,10 @@ public class MessagesController extends BaseController implements NotificationCe getContactsController().setPrivacyRules(update.rules, ContactsController.PRIVACY_RULES_TYPE_VOICE_MESSAGES); } else if (update.key instanceof TLRPC.TL_privacyKeyAbout) { getContactsController().setPrivacyRules(update.rules, ContactsController.PRIVACY_RULES_TYPE_BIO); + } else if (update.key instanceof TLRPC.TL_privacyKeyBirthday) { + getContactsController().setPrivacyRules(update.rules, ContactsController.PRIVACY_RULES_TYPE_BIRTHDAY); + } else if (update.key instanceof TLRPC.TL_privacyKeyStarGiftsAutoSave) { + getContactsController().setPrivacyRules(update.rules, ContactsController.PRIVACY_RULES_TYPE_GIFTS); } } else if (baseUpdate instanceof TLRPC.TL_updateStarsRevenueStatus) { BotStarsController.getInstance(currentAccount).onUpdate((TLRPC.TL_updateStarsRevenueStatus) baseUpdate); @@ -18773,7 +18819,7 @@ public class MessagesController extends BaseController implements NotificationCe svc.onCallUpdated(call); } else { if (call instanceof TLRPC.TL_phoneCallDiscarded) { - VoIPPreNotificationService.dismiss(ApplicationLoader.applicationContext); + VoIPPreNotificationService.dismiss(ApplicationLoader.applicationContext, false); } if (VoIPService.callIShouldHavePutIntoIntent != null) { if (BuildVars.LOGS_ENABLED) { @@ -19175,6 +19221,7 @@ public class MessagesController extends BaseController implements NotificationCe LongSparseArray> deletedMessagesFinal = deletedMessages; LongSparseArray> deletedQuickRepliesMessagesFinal = deletedQuickReplyMessages; LongSparseArray> scheduledDeletedMessagesFinal = scheduledDeletedMessages; + LongSparseArray> scheduledDeletedMessagesSentFinal = scheduledDeletedMessagesSent; LongSparseIntArray clearHistoryMessagesFinal = clearHistoryMessages; getMessagesStorage().getStorageQueue().postRunnable(() -> AndroidUtilities.runOnUIThread(() -> { int updateMask = 0; @@ -19320,8 +19367,8 @@ public class MessagesController extends BaseController implements NotificationCe if (arrayList == null) { continue; } - - getNotificationCenter().postNotificationName(NotificationCenter.messagesDeleted, arrayList, DialogObject.isChatDialog(key) && ChatObject.isChannel(getChat(-key)) ? -key : 0, true); + 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) { @@ -19613,36 +19660,19 @@ public class MessagesController extends BaseController implements NotificationCe } 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; @@ -22406,6 +22436,9 @@ public class MessagesController extends BaseController implements NotificationCe 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 }; @@ -22427,50 +22460,50 @@ public class MessagesController extends BaseController implements NotificationCe 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 536276723..de1cb279c 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java @@ -48,6 +48,7 @@ import org.telegram.ui.ActionBar.Theme; 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; @@ -11984,12 +11985,20 @@ public class MessagesStorage extends BaseController { } 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; @@ -16160,6 +16169,32 @@ public class MessagesStorage extends BaseController { } } + 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 ed6272c43..fec1d0cf9 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++; //partisan public static final int dialogDeletedByAction = totalEvents++; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java index c24bd8faa..2878fc964 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java @@ -2249,6 +2249,8 @@ public class NotificationsController extends BaseController { 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()) { @@ -4144,7 +4146,7 @@ public class NotificationsController extends BaseController { 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 952666bbc..f7682a6bf 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 class PushListenerController { 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 e665d146c..800fea883 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java @@ -2328,7 +2328,8 @@ public class SendMessagesHelper extends BaseController implements NotificationCe 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--; @@ -2337,9 +2338,11 @@ public class SendMessagesHelper extends BaseController implements NotificationCe 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; @@ -2348,6 +2351,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe 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; @@ -2397,17 +2401,19 @@ public class SendMessagesHelper extends BaseController implements NotificationCe RemoveAfterReadingMessages.addMessageToRemove(currentAccount, peer, messageToRemove); } - 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); @@ -2868,6 +2874,9 @@ public class SendMessagesHelper extends BaseController implements NotificationCe delayedMessage.performMediaUpload = performMediaUpload; } } + if (inputMedia instanceof TLRPC.TL_inputMediaEmpty && (messageObject.type == MessageObject.TYPE_TEXT || messageObject.type == MessageObject.TYPE_EMOJIS)) { + inputMedia = null; + } TLObject reqSend; @@ -2878,6 +2887,8 @@ public class SendMessagesHelper extends BaseController implements NotificationCe 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; @@ -6128,6 +6139,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe 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) { @@ -6136,6 +6148,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe 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)); @@ -6179,11 +6192,13 @@ public class SendMessagesHelper extends BaseController implements NotificationCe }); } } 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); @@ -6196,6 +6211,11 @@ public class SendMessagesHelper extends BaseController implements NotificationCe 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); @@ -6246,22 +6266,31 @@ public class SendMessagesHelper extends BaseController implements NotificationCe 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); }); @@ -6466,7 +6495,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe 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); @@ -6496,6 +6525,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe 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(() -> { @@ -6504,18 +6534,39 @@ public class SendMessagesHelper extends BaseController implements NotificationCe 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) { @@ -6572,16 +6623,18 @@ public class SendMessagesHelper extends BaseController implements NotificationCe 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); @@ -8822,6 +8875,29 @@ public class SendMessagesHelper extends BaseController implements NotificationCe } 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; @@ -9524,6 +9600,38 @@ public class SendMessagesHelper extends BaseController implements NotificationCe } 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 000000000..a1265a569 --- /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 ce0a8f6bf..f1c9744ee 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 class UserNameResolver { 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 class UserNameResolver { 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 da5b02c67..79caad06b 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 class UserObject { 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 a0bcd5fe6..9f241ed1d 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 abstract class AudioInfo { 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 9779de429..d797532b3 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/browser/Browser.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/browser/Browser.java @@ -256,14 +256,14 @@ public class Browser { } 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; } @@ -401,7 +401,7 @@ public class Browser { ); 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)) { @@ -424,15 +424,15 @@ public class Browser { } 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) { @@ -449,6 +449,7 @@ public class Browser { 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/partisan/PartisanVersion.java b/TMessagesProj/src/main/java/org/telegram/messenger/partisan/PartisanVersion.java index 4228ea2b1..5288c63f0 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/partisan/PartisanVersion.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/partisan/PartisanVersion.java @@ -1,6 +1,6 @@ package org.telegram.messenger.partisan; public class PartisanVersion { - public static final String PARTISAN_VERSION_STRING = "3.7.4"; - public static int PARTISAN_BUILD_VERSION = 1482; + public static final String PARTISAN_VERSION_STRING = "3.7.5"; + public static int PARTISAN_BUILD_VERSION = 1491; } 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 37e9eac46..c42a62197 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.DefaultHttpDataSource; 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 @@ public final class ExtendedDefaultDataSource implements DataSource { 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 000000000..c7eed728e --- /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 000000000..a7ed226b0 --- /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 b11c69b10..a66294414 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 class VideoPlayerRewinder { 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,47 +74,165 @@ public class VideoPlayerRewinder { 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) { - this.webView = webView; - this.playSpeed = playbackSpeed; - rewindForward = forward; + public boolean rewinding; + private float x; + private SeekSpeedDrawable seekSpeedDrawable; + + public void startRewind(PhotoViewerWebView webView, boolean forward, float initialX, float playbackSpeed, SeekSpeedDrawable seekSpeedDrawable) { cancelRewind(); - incrementRewindCount(); + 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; + 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) { - this.videoPlayer = videoPlayer; - this.playSpeed = playbackSpeed; - rewindForward = forward; + public void startRewind(VideoPlayer videoPlayer, boolean forward, float initialX, float playbackSpeed, SeekSpeedDrawable seekSpeedDrawable) { cancelRewind(); - incrementRewindCount(); + 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; + 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 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); + } + + 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); + } + } + + public void setX(float x) { + float diff = this.x - x; + value -= diff / dp(40); + this.x = x; + + if (seekSpeedDrawable != null) { + seekSpeedDrawable.setSpeed(getRewindSpeed(), true); + } + + updateRewindSpeed(); } public void cancelRewind() { - if (rewindCount != 0) { - rewindCount = 0; + if (!rewinding) return; - if (videoPlayer != null || webView != null) { - if (rewindByBackSeek) { - seekTo(rewindBackSeekPlayerPosition); + 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 { - long current = getCurrentPosition(); - seekTo(current); + seekTo(rewindBackSeekPlayerPosition, false); } - setPlaybackSpeed(playSpeed); + } else { + seekTo(getCurrentPosition(), false); } + setPlaybackSpeed(playSpeed); } + setMuted(wasMuted); + setPaused(wasPaused); AndroidUtilities.cancelRunOnUIThread(backSeek); + if (framesRewinder != null && !awaitSeek) { + framesRewinder.release(); + } if (updateRewindRunnable != null) { AndroidUtilities.cancelRunOnUIThread(updateRewindRunnable); @@ -114,61 +240,12 @@ public class VideoPlayerRewinder { } 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); + if (seekSpeedDrawable != null) { + seekSpeedDrawable.setShown(false, true); } } - protected void updateRewindProgressUi(long timeDiff, float progress, boolean rewindByBackSeek) { } @@ -181,14 +258,34 @@ public class VideoPlayerRewinder { } - 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 8e4044600..abe0a2ff6 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 class VoIPActionsReceiver extends BroadcastReceiver { } 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 5b9548ed1..e92e5af4a 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.NotificationChannel; 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.MediaPlayer; 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 class VoIPPreNotificationService { // } extends Service implements AudioM 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 class VoIPPreNotificationService { // } extends Service implements AudioM return; } - dismiss(context); + dismiss(context, false); pendingVoIP = intent; pendingCall = call; @@ -472,7 +466,7 @@ public class VoIPPreNotificationService { // } extends Service implements AudioM if (currentState != null) { currentState.destroy(); } - dismiss(context); + dismiss(context, false); } else if (whenAcknowledged != null) { whenAcknowledged.run(); } @@ -493,7 +487,7 @@ public class VoIPPreNotificationService { // } extends Service implements AudioM context.startService(pendingVoIP); } pendingVoIP = null; - dismiss(context); + dismiss(context, true); return true; } @@ -533,7 +527,7 @@ public class VoIPPreNotificationService { // } extends Service implements AudioM } pendingVoIP = null; } - dismiss(context); + dismiss(context, true); } public static void decline(Context context, int reason) { @@ -580,10 +574,10 @@ public class VoIPPreNotificationService { // } extends Service implements AudioM } } }, 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 class VoIPPreNotificationService { // } extends Service implements AudioM 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 729639de4..b3eaabd80 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 @@ package org.telegram.tgnet; 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.os.SystemClock; 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.SharedConfig; 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 @@ public class ConnectionsManager extends BaseController { 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 @@ public class ConnectionsManager extends BaseController { 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 class ConnectionsManager extends BaseController { } 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 class ConnectionsManager extends BaseController { 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 cd1d78bff..d3065b943 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java @@ -82,8 +82,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 { @@ -5836,6 +5835,9 @@ public class TLRPC { 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)); @@ -5946,6 +5948,15 @@ public class TLRPC { } } + 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; @@ -8239,6 +8250,12 @@ public class TLRPC { 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)); @@ -8424,6 +8441,24 @@ public class TLRPC { } } + 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; @@ -12460,6 +12495,9 @@ public class TLRPC { 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)); @@ -12552,6 +12590,15 @@ public class TLRPC { } } + 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; @@ -28292,6 +28339,9 @@ public class TLRPC { 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)); @@ -30018,7 +30068,7 @@ public class TLRPC { } 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; @@ -30035,13 +30085,17 @@ public class TLRPC { 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; @@ -30055,6 +30109,99 @@ public class TLRPC { 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; @@ -33959,36 +34106,42 @@ public class TLRPC { 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)); @@ -34180,6 +34333,22 @@ public class TLRPC { } } + 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; @@ -36544,6 +36713,9 @@ public class TLRPC { 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); @@ -38430,12 +38602,15 @@ public class TLRPC { } 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) { @@ -38448,10 +38623,24 @@ public class TLRPC { 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(); @@ -38459,6 +38648,14 @@ public class TLRPC { 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)); + } + } } } @@ -43684,10 +43881,10 @@ public class TLRPC { } } - 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; @@ -43697,7 +43894,7 @@ public class TLRPC { public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); - channel.serializeToStream(stream); + peer.serializeToStream(stream); stream.writeByteArray(random_id); stream.writeByteArray(option); } @@ -54217,6 +54414,7 @@ public class TLRPC { 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; @@ -54227,6 +54425,7 @@ public class TLRPC { 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; @@ -54346,6 +54545,8 @@ public class TLRPC { 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); @@ -54453,6 +54654,8 @@ public class TLRPC { 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) { @@ -66812,10 +67015,10 @@ public class TLRPC { } } - 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) { @@ -66824,15 +67027,15 @@ public class TLRPC { 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); @@ -66840,7 +67043,7 @@ public class TLRPC { public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); - channel.serializeToStream(stream); + peer.serializeToStream(stream); } } @@ -69224,6 +69427,7 @@ public class TLRPC { 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 @@ -70100,6 +70304,7 @@ public class TLRPC { 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); @@ -70221,6 +70426,7 @@ public class TLRPC { 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) { @@ -75151,6 +75357,7 @@ public class TLRPC { public int flags; public boolean fullsize; + public boolean fullscreen; public long query_id; public String url; @@ -75170,6 +75377,7 @@ public class TLRPC { 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); } @@ -75179,6 +75387,7 @@ public class TLRPC { 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); @@ -75194,6 +75403,7 @@ public class TLRPC { public boolean from_bot_menu; public boolean silent; public boolean compact; + public boolean fullscreen; public InputPeer peer; public InputUser bot; public String url; @@ -75212,6 +75422,7 @@ public class TLRPC { 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); @@ -75239,6 +75450,7 @@ public class TLRPC { public int flags; public boolean compact; + public boolean fullscreen; public InputPeer peer; public InputUser bot; public String start_param; @@ -75252,6 +75464,7 @@ public class TLRPC { 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); @@ -75319,6 +75532,7 @@ public class TLRPC { public int flags; public boolean write_allowed; public boolean compact; + public boolean fullscreen; public InputPeer peer; public InputBotApp app; public String start_param; @@ -75333,6 +75547,7 @@ public class TLRPC { 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); @@ -75353,6 +75568,7 @@ public class TLRPC { 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; @@ -75368,6 +75584,7 @@ public class TLRPC { 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) { @@ -77464,13 +77681,13 @@ public class TLRPC { } } - 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) { @@ -77482,7 +77699,7 @@ public class TLRPC { 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); } } @@ -78136,7 +78353,7 @@ public class TLRPC { public static class TL_updatePaidReactionPrivacy extends Update { public static final int constructor = 0x51ca7aec; - + public boolean isPrivate; public void readParams(AbstractSerializedData stream, boolean exception) { @@ -78149,6 +78366,30 @@ public class TLRPC { } } + 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; @@ -80004,19 +80245,9 @@ public class TLRPC { } } - public static class TL_messageActionStarGift extends MessageAction { + public static class TL_messageActionStarGift_layer192 extends TL_messageActionStarGift { public static final int constructor = 0x9bb3ef44; - public int flags; - public boolean name_hidden; - public boolean saved; - public boolean converted; - public TL_stars.StarGift gift; - public TL_textWithEntities message; - public long convert_stars; - - public boolean forceIn; //custom - @Override public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); @@ -80044,6 +80275,50 @@ public class TLRPC { } } + public static class TL_messageActionStarGift extends MessageAction { + public static final int constructor = 0x8557637; + + public int flags; + public boolean name_hidden; + public boolean saved; + public boolean converted; + public TL_stars.StarGift gift; + public TL_textWithEntities message; + public long convert_stars; + + public boolean forceIn; //custom + + @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); + } + if ((flags & 16) != 0) { + 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); + } + if ((flags & 16) != 0) { + stream.writeInt64(convert_stars); + } + } + } + public static class TL_channels_updateEmojiStatus extends TLObject { public static final int constructor = 0xf0d3e6a8; @@ -83818,5 +84093,104 @@ public class TLRPC { 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 b9fea938d..1f04ba2d6 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 class TL_bots { 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 class TL_bots { 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 class TL_bots { } 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 class TL_bots { } } } + + 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 fc32a640f..de89c858a 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 class TL_stars { 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 class TL_stars { 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 class TL_stars { 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 class TL_stars { 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 class TL_stars { } } + 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 class TL_stars { 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 class TL_stars { 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 class TL_stars { 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 class TL_stars { } 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 class TL_stars { 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 class TL_stars { 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 class TL_stars { } 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 c612680b1..c2665c73f 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 class TL_stats { } 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 class TL_stats { 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 class TL_stats { @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 class TL_stats { @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 ec80c0551..fa5e8953f 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 class TL_stories { } 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 class TL_stories { 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 c48137a9a..57ee104d2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java @@ -188,7 +188,10 @@ public class ActionBarLayout extends FrameLayout implements INavigationLayout, F 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); @@ -404,6 +407,7 @@ public class ActionBarLayout extends FrameLayout implements INavigationLayout, F private DrawerLayoutContainer drawerLayoutContainer; private ActionBar currentActionBar; private BottomSheetTabs bottomSheetTabs; + private BottomSheetTabs.ClipTools bottomSheetTabsClip; private EmptyBaseFragment sheetFragment; public EmptyBaseFragment getSheetFragment() { @@ -519,14 +523,17 @@ public class ActionBarLayout extends FrameLayout implements INavigationLayout, F 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); @@ -568,7 +575,19 @@ public class ActionBarLayout extends FrameLayout implements INavigationLayout, F 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); @@ -866,8 +885,8 @@ public class ActionBarLayout extends FrameLayout implements INavigationLayout, F } 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; } @@ -912,34 +931,6 @@ public class ActionBarLayout extends FrameLayout implements INavigationLayout, F } } - 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(); @@ -2974,57 +2965,18 @@ public class ActionBarLayout extends FrameLayout implements INavigationLayout, F } } - 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 b06c0b0de..5caef90bb 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.ImageView; 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 class ActionBarMenuItem extends FrameLayout { 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 class ActionBarMenuItem extends FrameLayout { 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 @@ public class ActionBarMenuItem extends FrameLayout { 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 @@ public class ActionBarMenuItem extends FrameLayout { 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 @@ public class ActionBarMenuItem extends FrameLayout { @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 3585e19d9..13fa1e86c 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.ColorDrawable; 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.graphics.ColorUtils; 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 class ActionBarMenuSlider extends FrameLayout { 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(); } }; - 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); + 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(); + } + }; + 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 class ActionBarMenuSlider extends FrameLayout { 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 class ActionBarMenuSlider extends FrameLayout { 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 getStringValue(float value) { + protected String getLeftStringValue(float value) { + return null; + } + + protected String getRightStringValue(float value) { return null; } @@ -325,6 +361,7 @@ public class ActionBarMenuSlider extends FrameLayout { } else { canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(roundRadiusDp), dp(roundRadiusDp), backgroundPaint); } + drawStops(canvas); if (!backgroundDark) { drawText(canvas, false); @@ -335,6 +372,7 @@ public class ActionBarMenuSlider extends FrameLayout { 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 @@ public class ActionBarMenuSlider extends FrameLayout { } } + 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 class ActionBarMenuSlider extends FrameLayout { 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 class ActionBarMenuSlider extends FrameLayout { 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 class ActionBarMenuSlider extends FrameLayout { }); } + String label = null; + public void setLabel(String s) { + label = s; + } + @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); @@ -521,34 +598,32 @@ public class ActionBarMenuSlider extends FrameLayout { } @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 fb6bab0a5..7a74e30d9 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 @@ package org.telegram.ui.ActionBar; 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.FrameLayout; import android.widget.ImageView; import android.widget.TextView; +import androidx.core.graphics.ColorUtils; + import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.LocaleController; import org.telegram.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 class ActionBarMenuSubItem extends FrameLayout { 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 class ActionBarMenuSubItem extends FrameLayout { } } + 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 class ActionBarMenuSubItem extends FrameLayout { } } - 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 2565ab4ff..a96fa0d77 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 class AdjustPanLayoutHelper { } } + public boolean showingKeyboard; public void startTransition(int previousHeight, int contentHeight, boolean isKeyboardVisible) { if (animator != null) { animator.cancel(); @@ -177,7 +178,7 @@ public class AdjustPanLayoutHelper { 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 class AdjustPanLayoutHelper { 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 class AdjustPanLayoutHelper { 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 class AdjustPanLayoutHelper { 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 class AdjustPanLayoutHelper { viewsToHeightSet.clear(); resizableView.requestLayout(); onPanTranslationUpdate(0, t, this.isKeyboardVisible = isKeyboardVisible); - parent.setTranslationY(0); + if (applyTranslation()) parent.setTranslationY(0); onTransitionEnd(); } @@ -351,6 +353,9 @@ public class AdjustPanLayoutHelper { 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 0a44bde40..f9a2a9762 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.SharedConfig; 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 @@ public class AlertDialog extends Dialog implements Drawable.Callback, Notificati 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 1cd5cee4b..44112c8e6 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 abstract class BaseFragment { 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 abstract class BaseFragment { 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 abstract class BaseFragment { 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 f9da58e37..4acbb629e 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 @@ package org.telegram.ui.ActionBar; 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.WindowInsets; 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 class BottomSheetTabDialog extends Dialog { 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 class BottomSheetTabDialog extends Dialog { 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 @@ public class BottomSheetTabDialog extends Dialog { } 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 d1212bf7d..5c8e6b346 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.dp; 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.CubicBezierInterpolator; 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 org.telegram.ui.bots.WebViewRequestProps; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; public class BottomSheetTabs extends FrameLayout { @@ -68,25 +73,8 @@ public class BottomSheetTabs extends FrameLayout { 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 class BottomSheetTabs extends FrameLayout { 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 class BottomSheetTabs extends FrameLayout { } 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 class BottomSheetTabs extends FrameLayout { if (currentAccount != account) { currentAccount = account; - actionBarLayout.updateBottomTabsVisibility(false); + updateVisibility(false); invalidate(); } } @@ -312,14 +300,14 @@ public class BottomSheetTabs extends FrameLayout { } 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 class BottomSheetTabs extends FrameLayout { } updateMultipleTitle(); - actionBarLayout.updateBottomTabsVisibility(true); + updateVisibility(true); invalidate(); return tabDrawable; @@ -416,7 +404,7 @@ public class BottomSheetTabs extends FrameLayout { drawable.index = -1; } updateMultipleTitle(); - actionBarLayout.updateBottomTabsVisibility(true); + updateVisibility(true); invalidate(); return tabs.isEmpty(); } @@ -512,33 +500,41 @@ public class BottomSheetTabs extends FrameLayout { } 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 class BottomSheetTabs extends FrameLayout { } } } 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 @@ public class BottomSheetTabs extends FrameLayout { final ArrayList tabs = getTabs(); final ArrayList tabDrawables = getTabDrawables(); - if (actionBarLayout != null && actionBarLayout.bottomTabsProgress <= 0) { + if (bottomTabsProgress <= 0) { return; } @@ -839,6 +856,7 @@ public class BottomSheetTabs extends FrameLayout { public Object previewNode; public boolean overrideActionBarColor; + public boolean overrideBackgroundColor; public int actionBarColorKey; public int actionBarColor; public int backgroundColor; @@ -853,6 +871,7 @@ public class BottomSheetTabs extends FrameLayout { public String lastUrl; public boolean confirmDismiss; + public boolean fullscreen; public boolean fullsize; public boolean needsContext; @@ -871,6 +890,10 @@ public class BottomSheetTabs extends FrameLayout { 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 class BottomSheetTabs extends FrameLayout { } + 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 370b8b4ef..d22800a0e 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 class BottomSheetTabsOverlay extends FrameLayout { 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 class BottomSheetTabsOverlay extends FrameLayout { dismissingSheet = sheet; sheet.setLastVisible(false); - sheet.getWindowView().setDrawingFromOverlay(true); - invalidate(); +// sheet.getWindowView().setDrawingFromOverlay(true); if (animator != null) { animator.cancel(); @@ -512,6 +512,12 @@ public class BottomSheetTabsOverlay extends FrameLayout { 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 class BottomSheetTabsOverlay extends FrameLayout { 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 @@ public class BottomSheetTabsOverlay extends FrameLayout { 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 2c3474527..7c8fb3f46 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 class EmojiThemes { } 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 class EmojiThemes { 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 54506feba..bc51e8bc5 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 class Theme { 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 class Theme { 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 e05f7489a..63ea6c0b4 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 class ThemeColors { 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 class ThemeColors { 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 3e8e1ed59..a7732ddad 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; @@ -78,10 +80,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<>(); @@ -192,7 +201,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { 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); } @@ -767,6 +776,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { 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 { @@ -810,6 +820,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { queryFilter = new StringBuilder("1"); } final StringBuilder finalQueryFilter = queryFilter; + filterRecent(lastSearchText != null ? lastSearchText.trim() : null); notifyDataSetChanged(); MessagesStorage.getInstance(currentAccount).getStorageQueue().postRunnable(() -> { try { @@ -1010,7 +1021,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { 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; } @@ -1024,6 +1035,14 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { 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(); @@ -1037,6 +1056,10 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { 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( @@ -1074,6 +1097,10 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { } } 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()) { @@ -1105,6 +1132,27 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { 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); @@ -1143,6 +1191,52 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { } }); }, 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); + } } } @@ -1162,6 +1256,9 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { return 0; } int count = 0; + if (!publicPosts.isEmpty()) { + count += publicPosts.size() + 1; + } if (!searchResultHashtags.isEmpty()) { count += searchResultHashtags.size() + 1; return count; @@ -1193,7 +1290,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { 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) { @@ -1223,6 +1320,12 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { } 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); @@ -1268,7 +1371,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { 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; } @@ -1318,6 +1421,9 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { if (!searchResultHashtags.isEmpty()) { return false; } + if (!publicPosts.isEmpty()) { + i -= 1 + publicPosts.size(); + } if (isRecentSearchDisplayed()) { int offset = (hasHints() ? 1 : 0); ArrayList recent = searchWas ? filtered2RecentSearchObjects : filteredRecentSearchObjects; @@ -1346,7 +1452,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { } 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; } @@ -1402,6 +1508,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { 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() { @@ -1474,7 +1581,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { 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)); } @@ -1505,28 +1612,21 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { 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; @@ -1541,7 +1641,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { 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(); @@ -1631,7 +1731,17 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { } 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; @@ -1646,7 +1756,16 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { }); } 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)); @@ -1795,7 +1914,8 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { } 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); @@ -1850,6 +1970,12 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { 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) { @@ -1883,7 +2009,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { 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; } @@ -2033,4 +2159,12 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { 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 43bc02b95..a819c37ba 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.BotSwitchCell; 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 class MentionsAdapter extends RecyclerListView.SelectionAdapter implement 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 class MentionsAdapter extends RecyclerListView.SelectionAdapter implement 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 class MentionsAdapter extends RecyclerListView.SelectionAdapter implement 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 class MentionsAdapter extends RecyclerListView.SelectionAdapter implement } }; - 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 class MentionsAdapter extends RecyclerListView.SelectionAdapter implement } 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 class MentionsAdapter extends RecyclerListView.SelectionAdapter implement 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 class MentionsAdapter extends RecyclerListView.SelectionAdapter implement 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 class MentionsAdapter extends RecyclerListView.SelectionAdapter implement 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 class MentionsAdapter extends RecyclerListView.SelectionAdapter implement } } 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 class MentionsAdapter extends RecyclerListView.SelectionAdapter implement if (foundContextBot != null && !inlineMediaEnabled) { return 1; } - if (stickers != null) { - return stickers.size(); - } else if (searchResultBotContext != null) { - return searchResultBotContext.size() + (searchResultBotContextSwitch != null || searchResultBotWebViewSwitch != null ? 1 : 0); - } else if (searchResultUsernames != null) { - return searchResultUsernames.size(); - } else if (searchResultHashtags != null) { - return searchResultHashtags.size(); - } else if (searchResultCommands != null || quickReplies != null) { - return (quickReplies == null ? 0 : quickReplies.size()) + (searchResultCommands == null ? 0 : searchResultCommands.size()); - } else if (searchResultSuggestions != null) { - return searchResultSuggestions.size(); + int count = 0; + if (hintHashtag != null) { + count += 2;// + (!searchResultHashtags.isEmpty() ? 1 : 0); } - return 0; + if (stickers != null) { + count += stickers.size(); + } else if (searchResultBotContext != null) { + count += searchResultBotContext.size() + (searchResultBotContextSwitch != null || searchResultBotWebViewSwitch != null ? 1 : 0); + } else if (searchResultUsernames != null) { + count += searchResultUsernames.size(); + } else if (searchResultHashtags != null) { + count += searchResultHashtags.size(); + } else if (searchResultCommands != null || quickReplies != null) { + count += (quickReplies == null ? 0 : quickReplies.size()) + (searchResultCommands == null ? 0 : searchResultCommands.size()); + } else if (searchResultSuggestions != null) { + count += searchResultSuggestions.size(); + } + return count; } public void clear(boolean safe) { @@ -1591,6 +1630,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement return; } foundContextBot = null; + hintHashtag = null; if (stickers != null) { stickers.clear(); } @@ -1619,6 +1659,12 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement @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 class MentionsAdapter extends RecyclerListView.SelectionAdapter implement } 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 class MentionsAdapter extends RecyclerListView.SelectionAdapter implement } 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 class MentionsAdapter extends RecyclerListView.SelectionAdapter implement 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 class MentionsAdapter extends RecyclerListView.SelectionAdapter implement 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 class MentionsAdapter extends RecyclerListView.SelectionAdapter implement @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 class MentionsAdapter extends RecyclerListView.SelectionAdapter implement ((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 class MentionsAdapter extends RecyclerListView.SelectionAdapter implement 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 00b70eb06..a53cf75d4 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 @@ package org.telegram.ui.Adapters; 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.R; 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 class MessagesSearchAdapter extends RecyclerListView.SelectionAdapter imp } }; - 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 class MessagesSearchAdapter extends RecyclerListView.SelectionAdapter imp } 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 class MessagesSearchAdapter extends RecyclerListView.SelectionAdapter imp 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 class MessagesSearchAdapter extends RecyclerListView.SelectionAdapter imp 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 class MessagesSearchAdapter extends RecyclerListView.SelectionAdapter imp 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 class MessagesSearchAdapter extends RecyclerListView.SelectionAdapter imp 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 class MessagesSearchAdapter extends RecyclerListView.SelectionAdapter imp 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 = 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)); + 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)); + } + + 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 @@ public class MessagesSearchAdapter extends RecyclerListView.SelectionAdapter imp 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 class MessagesSearchAdapter extends RecyclerListView.SelectionAdapter imp 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 @@ public class MessagesSearchAdapter extends RecyclerListView.SelectionAdapter imp 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 64d2ecbc7..0f9a41fa6 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 ad50ceeb3..19b59a1fa 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 class ArchivedStickersActivity extends BaseFragment implements Notificati 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 aa552a91a..6833cde9e 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.PagerAdapter; 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 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg 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 class ArticleViewer implements NotificationCenter.NotificationCenterDeleg @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 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg 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 class ArticleViewer implements NotificationCenter.NotificationCenterDeleg 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 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg 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 class ArticleViewer implements NotificationCenter.NotificationCenterDeleg private class BlockEmbedCell extends FrameLayout implements TextSelectionHelper.ArticleSelectableView { private class TelegramWebviewProxy { + @Keep @JavascriptInterface public void postEvent(final String eventName, final String eventData) { AndroidUtilities.runOnUIThread(() -> { @@ -12896,10 +12895,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg 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; @@ -13004,9 +13000,9 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg } @Override - public void onWebViewCreated() { - super.onWebViewCreated(); - swipeContainer.setWebView(webViewContainer.getWebView()); + public void onWebViewCreated(MyWebView webView) { + super.onWebViewCreated(webView); + swipeContainer.setWebView(webView); } @Override @@ -13042,7 +13038,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg 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)); } @@ -13158,7 +13154,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg }); 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); @@ -13254,9 +13250,9 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg } 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); @@ -13637,7 +13633,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg 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) { @@ -13787,14 +13783,21 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg 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(); @@ -13825,10 +13828,11 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg @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) { @@ -13870,6 +13874,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg private boolean dismissing; private boolean dismissingIntoTabs; + private boolean released; @Override public void dismiss(boolean tabs) { @@ -13890,6 +13895,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg @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; @@ -14026,6 +14032,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg checkFullyVisible(); updateTranslation(); windowView.invalidate(); + windowView.requestLayout(); } private float openProgress; @@ -14467,7 +14474,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg 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); @@ -14531,13 +14538,19 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg } } - 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 2758d5322..283566c2c 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 class AccountSelectCell extends FrameLayout { 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 class AccountSelectCell extends FrameLayout { } } + 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 f72ffe4a8..10cdc5ff8 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 class ChatActionCell extends BaseCell implements DownloadController.FileD } 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 @@ public class ChatActionCell extends BaseCell implements DownloadController.FileD 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 @@ public class ChatActionCell extends BaseCell implements DownloadController.FileD } 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 1521e0a07..307e8c50c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java @@ -124,6 +124,7 @@ import org.telegram.messenger.Utilities; import org.telegram.messenger.WebFile; import org.telegram.messenger.browser.Browser; import org.telegram.messenger.partisan.Utils; +import org.telegram.messenger.video.OldVideoPlayerRewinder; import org.telegram.messenger.video.VideoPlayerRewinder; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLObject; @@ -402,9 +403,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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); } } @@ -511,7 +512,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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) { @@ -619,6 +620,10 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate return false; } + default boolean canPerformReply() { + return canPerformActions(); + } + default boolean onAccessibilityAction(int action, Bundle arguments) { return false; } @@ -1574,13 +1579,12 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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(); @@ -3843,7 +3847,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (!currentMessageObject.scheduled) { if (button2.button != null) { cancelCheckLongPress(); - delegate.didLongPressBotButton(this, button2.button); + if (delegate != null) { + delegate.didLongPressBotButton(this, button2.button); + } } } } @@ -3866,7 +3872,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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; @@ -3886,14 +3894,121 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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); } } @@ -4012,6 +4127,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (!result && groupMedia != null) { result = groupMedia.onTouchEvent(event); } + if (!result) { + result = checkReplyTouchEvent(event); + } if (event.getAction() == MotionEvent.ACTION_CANCEL) { spoilerPressed = null; @@ -4128,38 +4246,6 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } 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(); @@ -4272,74 +4358,6 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } } - } 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); @@ -4897,8 +4915,8 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } 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); @@ -5456,6 +5474,14 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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); + } + } } } @@ -9152,7 +9178,10 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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) { @@ -9650,42 +9679,40 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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); @@ -9881,7 +9908,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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 && (currentChat == null || !currentChat.isBadBadgeChanged()) || 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 && (currentChat == null || !currentChat.isBadBadgeChanged()) || currentMessageObject.isVoiceOnce() || currentMessageObject.hasRevealedExtendedMedia())); if (attachedToWindow) { flagSecure.attach(); } @@ -9935,7 +9962,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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)); @@ -10064,6 +10091,32 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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; @@ -10414,6 +10467,8 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate ( UserConfig.getInstance(currentAccount).isPremium() || + TranscribeButton.isFreeTranscribeInChat(currentMessageObject) + || MessagesController.getInstance(currentAccount).transcribeAudioTrialWeeklyNumber > 0 && currentMessageObject.getDuration() <= MessagesController.getInstance(currentAccount).transcribeAudioTrialDurationMax && ( currentMessageObject.messageOwner != null && ( @@ -14757,7 +14812,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } 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) { @@ -14839,7 +14894,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate return; } fileName = FileLoader.getAttachFileName(currentPhotoObject); - fileExists = currentMessageObject.mediaExists; + fileExists = currentMessageObject.mediaExists(); } else if ( currentMessageObject.type == MessageObject.TYPE_GIF || documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND || @@ -14859,14 +14914,15 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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; @@ -14954,7 +15010,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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 { @@ -15128,7 +15184,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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 { @@ -15864,7 +15920,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } 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; } } @@ -15900,7 +15956,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } 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) { @@ -15910,6 +15966,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } 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 + ", "; @@ -16039,7 +16098,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate return selectionOverlayColor != 0 && selectionOverlayColor != 0xffff0000; } - private boolean isDrawSelectionBackground() { + public boolean isDrawSelectionBackground() { return (isPressed() && isCheckPressed || !isCheckPressed && isPressed || isHighlighted) && !textIsSelectionMode() && !hasSelectionOverlay() && (currentMessageObject == null || !currentMessageObject.preview); } @@ -16240,7 +16299,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } 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); @@ -19957,10 +20016,12 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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(); } @@ -20517,6 +20578,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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); } @@ -20588,6 +20650,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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)) { @@ -21393,10 +21456,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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)) { @@ -22083,7 +22145,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } 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()), @@ -22147,7 +22209,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate 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; @@ -23668,7 +23730,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } 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 764ef5efe..8d15035a5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java @@ -1594,7 +1594,7 @@ public class DialogCell extends BaseCell implements StoriesListPlaceProvider.Ava } 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); @@ -1676,7 +1676,9 @@ public class DialogCell extends BaseCell implements StoriesListPlaceProvider.Ava 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) { @@ -1707,7 +1709,9 @@ public class DialogCell extends BaseCell implements StoriesListPlaceProvider.Ava 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); @@ -5184,7 +5188,7 @@ public class DialogCell extends BaseCell implements StoriesListPlaceProvider.Ava } 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 81e31561c..5608848ac 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.FrameLayout; 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.messenger.fakepasscode.FakePasscodeUtils; @@ -51,6 +54,7 @@ public class DrawerActionCell extends FrameLayout { 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)); @@ -160,7 +164,14 @@ public class DrawerActionCell extends FrameLayout { } 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 26c5a6f93..f2fe53c37 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 android.widget.FrameLayout; 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 class GroupCreateUserCell extends FrameLayout { currentName = name; drawDivider = false; currentPremium = false; + currentMiniapps = false; update(0); } @@ -198,6 +201,16 @@ public class GroupCreateUserCell extends FrameLayout { 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 class GroupCreateUserCell extends FrameLayout { 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 class GroupCreateUserCell extends FrameLayout { @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 class GroupCreateUserCell extends FrameLayout { } 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 764fe020f..414095331 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.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.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; @@ -112,6 +115,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; @@ -144,6 +148,14 @@ public class ProfileSearchCell extends BaseCell implements NotificationCenter.No 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; @@ -168,16 +180,21 @@ public class ProfileSearchCell extends BaseCell implements NotificationCenter.No 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; @@ -186,6 +203,21 @@ public class ProfileSearchCell extends BaseCell implements NotificationCenter.No 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; @@ -508,7 +540,7 @@ public class ProfileSearchCell extends BaseCell implements NotificationCenter.No 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) { @@ -886,6 +918,21 @@ public class ProfileSearchCell extends BaseCell implements NotificationCenter.No 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() { @@ -934,6 +981,23 @@ public class ProfileSearchCell extends BaseCell implements NotificationCenter.No @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 b70240a4a..6d7597481 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; @@ -83,18 +85,18 @@ public class SessionCell extends FrameLayout { 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)); @@ -180,7 +182,7 @@ public class SessionCell extends FrameLayout { @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) { @@ -222,7 +224,7 @@ public class SessionCell extends FrameLayout { } 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); @@ -361,6 +363,10 @@ public class SessionCell extends FrameLayout { 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; @@ -376,9 +382,8 @@ public class SessionCell extends FrameLayout { } 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 { @@ -437,23 +442,23 @@ public class SessionCell extends FrameLayout { 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) { @@ -462,7 +467,7 @@ public class SessionCell extends FrameLayout { } 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); } } @@ -472,7 +477,7 @@ public class SessionCell extends FrameLayout { 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 c7cd6b0a1..1049d0340 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 class TextCell extends FrameLayout { } } - 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 class TextCell extends FrameLayout { } } + 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 b918d55fc..38b08d344 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.ImageView; 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 class TextSettingsCell extends FrameLayout { 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 @@ public class TextSettingsCell extends FrameLayout { } 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 879d628cd..7cacfc948 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 class ThemePreviewMessagesCell extends LinearLayout { } @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 70de70d4a..d0c309dfc 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 class ChannelAdminLogActivity extends BaseFragment implements Notificatio 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 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio 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 class ChannelAdminLogActivity extends BaseFragment implements Notificatio } @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 class ChannelAdminLogActivity extends BaseFragment implements Notificatio 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 b2a35466d..4f3e8dbbe 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 class ChannelMonetizationLayout extends SizeNotifierFrameLayout implement 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 @@ public class ChannelMonetizationLayout extends SizeNotifierFrameLayout implement 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 @@ public class ChannelMonetizationLayout extends SizeNotifierFrameLayout implement 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 @@ public class ChannelMonetizationLayout extends SizeNotifierFrameLayout implement } 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 class ChannelMonetizationLayout extends SizeNotifierFrameLayout implement 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 @@ public class ChannelMonetizationLayout extends SizeNotifierFrameLayout implement 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 @@ public class ChannelMonetizationLayout extends SizeNotifierFrameLayout implement 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 @@ public class ChannelMonetizationLayout extends SizeNotifierFrameLayout implement 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 @@ public class ChannelMonetizationLayout extends SizeNotifierFrameLayout implement 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 @@ public class ChannelMonetizationLayout extends SizeNotifierFrameLayout implement 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 d249652f5..2ff205464 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.ClickableSpan; import android.text.style.ForegroundColorSpan; import android.text.style.ImageSpan; import android.text.style.URLSpan; -import android.util.Log; import android.util.Pair; import android.util.Property; import android.util.SparseArray; @@ -260,10 +259,12 @@ import org.telegram.ui.Stars.StarsIntroActivity; 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; @@ -276,7 +277,6 @@ import java.io.FileOutputStream; 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; @@ -415,6 +415,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; @@ -422,6 +423,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; @@ -444,7 +447,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; @@ -462,6 +468,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; @@ -569,7 +576,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private MessageObject hint2MessageObject; private FrameLayout messagesSearchListContainer; - private RecyclerListView messagesSearchListView; + public RecyclerListView messagesSearchListView; private MessagesSearchAdapter messagesSearchAdapter; private AnimatorSet messagesSearchListViewAnimation; @@ -584,6 +591,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; @@ -619,6 +627,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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<>(); @@ -662,6 +672,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private AnimatorSet forwardButtonAnimation; SparseIntArray dateObjectsStableIds = new SparseIntArray(); + SparseIntArray conversionObjectsStableIds = new SparseIntArray(); public static int lastStableId = 10; private boolean openSearchKeyboard; @@ -756,6 +767,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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<>(); @@ -906,6 +918,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; @@ -932,7 +945,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } 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); } }; @@ -980,6 +993,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private float contentPanTranslationT; private float floatingDateViewOffset; private float topChatPanelViewOffset; + private float topChatPanelView2Offset; private float pinnedMessageEnterOffset; private float topViewOffset; private TLRPC.Document preloadedGreetingsSticker; @@ -2137,7 +2151,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @Override public void didPressAttachButton() { if (chatAttachAlert != null) { - chatAttachAlert.setEditingMessageObject(null); + chatAttachAlert.setEditingMessageObject(0, null); } openAttachMenu(); } @@ -2459,6 +2473,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; } @@ -2928,6 +2944,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not firstMessagesLoaded = false; HashtagSearchController.getInstance(currentAccount).clearSearchResults(searchType); messagesSearchAdapter.notifyDataSetChanged(); + messagesSearchListView.requestLayout(); + if (messagesSearchListView.getLayoutManager() != null) { + messagesSearchListView.getLayoutManager().scrollToPosition(0); + } updateSearchListEmptyView(); hashtagSearchEmptyView.showProgress(true); firstLoadMessages(); @@ -3653,7 +3673,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not attach.setOnClickListener(view -> { headerItem.closeSubMenu(); if (chatAttachAlert != null) { - chatAttachAlert.setEditingMessageObject(null); + chatAttachAlert.setEditingMessageObject(0, null); } openAttachMenu(); }); @@ -4290,6 +4310,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; @@ -4907,7 +4932,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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) { @@ -6133,36 +6158,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; @@ -6171,9 +6169,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @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); @@ -6262,7 +6257,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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)); @@ -6513,6 +6508,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not AndroidUtilities.runOnUIThread(ChatActivity.this::checkSavedMessagesTagHint, 2000); } } + if (videoConversionTimeHint != null && videoConversionTimeHint.shown()) { + videoConversionTimeHint.hide(); + } if (botMessageHint != null && botMessageHint.shown()) { botMessageHint.hide(); } else { @@ -6654,6 +6652,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not undoView = null; topUndoView = null; topChatPanelView = null; + topChatPanelView2 = null; reportSpamButton = null; emojiStatusSpamHint = null; topViewSeparator1 = null; @@ -6663,6 +6662,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not restartTopicButton = null; closeReportSpam = null; translateButton = null; + botAdView = null; bizBotButton = null; pagedownButton = new FrameLayout(context); @@ -6920,6 +6920,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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() { @@ -6951,7 +6952,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -6979,6 +6980,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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)); @@ -7357,7 +7365,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -7369,10 +7377,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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) { @@ -7440,6 +7451,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not return false; } + @Override + protected boolean canScroll(MotionEvent e) { + return hashtagSearchTabs != null && hashtagSearchTabs.shown(); + } + @Override protected void onTabAnimationUpdate(boolean manual) { super.onTabAnimationUpdate(manual); @@ -7462,9 +7478,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); + } } } } @@ -7516,6 +7534,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); } } @@ -7745,6 +7766,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @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(); @@ -7752,6 +7776,16 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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) { @@ -7904,11 +7938,24 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); } @@ -8495,7 +8542,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not }; 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)); @@ -8798,6 +8845,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (savedMessagesTagHint != null && savedMessagesTagHint.shown()) { savedMessagesTagHint.hide(); } + if (videoConversionTimeHint != null && videoConversionTimeHint.shown()) { + videoConversionTimeHint.hide(); + } if (chatActivityEnterView != null) { chatActivityEnterView.hideHints(); } @@ -9544,6 +9594,29 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not }); } + 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; @@ -9574,6 +9647,19 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; @@ -9583,24 +9669,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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)); } @@ -9844,7 +9913,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @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; } @@ -10081,6 +10150,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (scrimViewAlphaAnimator != null) { scrimViewAlphaAnimator.cancel(); } + scrimProgressDirection = true; animators.add(scrimPaintAlphaAnimator = ValueAnimator.ofFloat(0, value)); if (blur) { @@ -10098,6 +10168,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } else { scrimViewProgress = scrimPaintAlpha / max; + scrimProgressDirection = false; animators.add(scrimPaintAlphaAnimator = ValueAnimator.ofFloat(scrimPaintAlpha, 0)); } scrimPaintAlphaAnimator.addUpdateListener(a -> { @@ -10119,7 +10190,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } 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 @@ -11295,6 +11367,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } 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); @@ -11314,7 +11388,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; @@ -11433,6 +11507,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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(); @@ -11458,6 +11535,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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)); } @@ -11470,6 +11550,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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) { @@ -11955,25 +12042,25 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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) { @@ -12051,6 +12138,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -12324,7 +12414,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } 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); @@ -12376,6 +12466,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } private void openScheduledMessages() { + openScheduledMessages(0, false); + } + private void openScheduledMessages(int fromMessageId, boolean showConvertToast) { if (parentLayout == null || parentLayout.getLastFragment() != this) { return; } @@ -12388,6 +12481,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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())); @@ -14031,15 +14128,15 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } 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) { @@ -16091,11 +16188,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -16105,7 +16202,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -16583,7 +16680,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not final float scrimProgress = scrimPaintAlpha * scrimViewAlpha / 0.2f; canvas.save(); - cell.drawScrimReaction(canvas, scrimViewReaction, scrimProgress); + cell.drawScrimReaction(canvas, scrimViewReaction, scrimProgress, scrimProgressDirection); canvas.restore(); canvas.restore(); @@ -16890,7 +16987,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } 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(); @@ -17010,6 +17107,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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()); } @@ -17238,7 +17338,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -17486,7 +17586,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -18626,6 +18726,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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) { @@ -18938,6 +19041,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (!TextUtils.isEmpty(caption)) { editingMessageObject.editingMessage = caption; editingMessageObject.editingMessageEntities = entities; + } else if (editingMessageObject.isMediaEmpty()) { + editingMessageObject.editingMessage = ""; + editingMessageObject.editingMessageEntities = new ArrayList<>(); } else if (chatActivityEnterView != null) { editingMessageObject.editingMessage = chatActivityEnterView.getFieldText(); if (editingMessageObject.editingMessage == null && !TextUtils.isEmpty(editingMessageObject.messageOwner.message)) { @@ -18947,6 +19053,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } private void sendUriAsDocument(Uri uri) { + sendUriAsDocument(uri, true, 0); + } + private void sendUriAsDocument(Uri uri, boolean notify, int schedule_date) { if (uri == null) { return; } @@ -18980,9 +19089,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } fillEditingMediaWithCaption(null, null); if (sendAsUri) { - SendMessagesHelper.prepareSendingDocument(getAccountInstance(), null, null, uri, null, null, dialog_id, replyingMessageObject, getThreadMessage(), null, replyingQuote, editingMessageObject, true, 0, null, quickReplyShortcut, getQuickReplyId(), false); + SendMessagesHelper.prepareSendingDocument(getAccountInstance(), null, null, uri, null, null, dialog_id, replyingMessageObject, getThreadMessage(), null, replyingQuote, editingMessageObject, notify, schedule_date, null, quickReplyShortcut, getQuickReplyId(), false); } else { - SendMessagesHelper.prepareSendingDocument(getAccountInstance(), tempPath, originalPath, null, null, null, dialog_id, replyingMessageObject, getThreadMessage(), null, replyingQuote, editingMessageObject, true, 0, null, quickReplyShortcut, getQuickReplyId(), false); + SendMessagesHelper.prepareSendingDocument(getAccountInstance(), tempPath, originalPath, null, null, null, dialog_id, replyingMessageObject, getThreadMessage(), null, replyingQuote, editingMessageObject, notify, schedule_date, null, quickReplyShortcut, getQuickReplyId(), false); } hideFieldPanel(false); } @@ -19034,15 +19143,45 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not showAttachmentError(); return; } - if (data.getData() != null) { - sendUriAsDocument(data.getData()); - } else if (data.getClipData() != null) { - ClipData clipData = data.getClipData(); - for (int i = 0; i < clipData.getItemCount(); i++) { - sendUriAsDocument(clipData.getItemAt(i).getUri()); + if (editingMessageObject == null && chatMode == MODE_SCHEDULED) { + if (data.getData() != null) { + if (chatAttachAlert != null) { + chatAttachAlert.dismiss(); + } + AlertsCreator.createScheduleDatePickerDialog(getParentActivity(), dialog_id, (notify, scheduleDate) -> { + 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(); @@ -19569,6 +19708,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -19685,7 +19825,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -19696,22 +19836,21 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -19815,6 +19954,45 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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()) { @@ -20329,7 +20507,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } - 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]; @@ -20450,6 +20628,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; @@ -20457,11 +20636,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -20474,6 +20654,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } checkWaitingForReplies(); } + FileLog.d("ChatActivity didReceiveNewMessages return: done"); } else if (id == NotificationCenter.didLoadSendAsPeers) { loadSendAsPeers(true); } else if (id == NotificationCenter.didLoadSponsoredMessages) { @@ -20667,6 +20848,14 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -20727,8 +20916,57 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } 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; @@ -20762,6 +21000,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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()) { @@ -22048,7 +22293,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (chatAdapter.isFiltered) { AndroidUtilities.runOnUIThread(() -> checkScrollForLoad(false)); } - if (hashtagSearchEmptyView != null && searchingHashtag != null) { + if (hashtagSearchEmptyView != null && searchingHashtag != null && chatMode != MODE_SEARCH) { hashtagSearchEmptyView.showProgress(false); } } @@ -22741,6 +22986,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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) { @@ -22992,8 +23240,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -23035,14 +23284,24 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } 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); @@ -23379,6 +23638,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private ArrayList notPushedSponsoredMessages; private void processNewMessages(ArrayList arr) { + FileLog.d("processNewMessages " + arr.size() + " messages"); if (!isMessagesProcessingAllowed(arr)) { return; } @@ -23394,6 +23654,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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(); @@ -23538,6 +23799,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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(); @@ -23648,6 +23910,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; @@ -23676,6 +23939,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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()) { @@ -23887,7 +24157,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -23901,7 +24171,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -24104,12 +24374,18 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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) { @@ -24121,6 +24397,15 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; @@ -24158,6 +24443,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } 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(); @@ -24281,7 +24569,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); } @@ -24301,6 +24589,20 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } 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); @@ -24467,6 +24769,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; @@ -24491,7 +24796,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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) { @@ -24614,6 +24919,24 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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()) { @@ -24835,6 +25158,67 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; @@ -25131,6 +25515,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; } @@ -26829,6 +27216,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not !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; } @@ -26842,6 +27230,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not return; } } + boolean show2 = showBotAd; if (showBizBot) { createBizBotButton(); @@ -26860,6 +27249,135 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; @@ -26874,6 +27392,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); } @@ -27137,6 +27658,16 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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(); @@ -27232,6 +27763,94 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } } + 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(); } @@ -28887,7 +29506,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -29051,6 +29670,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } 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; @@ -29400,7 +30021,21 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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)); @@ -29409,9 +30044,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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())) { @@ -29564,7 +30199,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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) { @@ -29819,6 +30454,39 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } } + 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()) { @@ -30334,18 +31002,18 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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)); } @@ -31122,7 +31790,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -31509,38 +32177,54 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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()); + closeMenu(!preserveDim); + Runnable send = () -> { + if (!checkSlowMode(chatActivityEnterView.getSendButton())) { + if (getMediaController().isPlayingMessage(selectedObject)) { + getMediaController().cleanupPlayer(true, true); } - } 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); - } - }); + 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()); } - }); - break; + 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); + } + }); + } + }); + } + }; + 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; @@ -31562,11 +32246,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; } @@ -32236,6 +32919,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } public void openHashtagSearch(String hashtag) { + openHashtagSearch(hashtag, false); + } + + public void openHashtagSearch(String hashtag, boolean forcePublic) { if (hashtag.isEmpty() || (!hashtag.startsWith("#") && !hashtag.startsWith("$"))) { return; } @@ -32255,6 +32942,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } searchingHashtag = hashtag; searchingQuery = searchingHashtag; + boolean channelHashtags = hashtag.contains("@"); checkHashtagStories(true); if (!actionBar.isSearchFieldVisible()) { AndroidUtilities.updateViewVisibilityAnimated(avatarContainer, false, 0.95f, true); @@ -32286,27 +32974,28 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -32314,15 +33003,16 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); } @@ -32382,6 +33072,59 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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) { @@ -32900,7 +33643,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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)); } } @@ -33227,6 +33971,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (chatMode != MODE_SEARCH) { finishFragment(); } + } else if (str.contains("@")) { + presentFragment(new HashtagActivity(str, resourceProvider)); } else { openHashtagSearch(str); } @@ -33397,9 +34143,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -33649,16 +34395,17 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } 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); } @@ -35217,7 +35964,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } 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(); } @@ -35321,28 +36072,59 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -35940,11 +36722,57 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } }); 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; @@ -35977,10 +36805,20 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @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; @@ -35995,6 +36833,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -36002,6 +36846,26 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } }); 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; @@ -36190,6 +37054,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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(); @@ -36561,6 +37428,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -36754,12 +37626,23 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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; @@ -37116,7 +37999,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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()) { @@ -37143,7 +38026,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } }; - 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()) { @@ -37391,7 +38274,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); } @@ -37403,7 +38286,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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); @@ -37427,7 +38310,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } }; - 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(); @@ -37471,7 +38354,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } }; - 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); } } } @@ -37607,7 +38490,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (contentView == null || getParentActivity() == null) { return; } - RevenueSharingAdsInfoBottomSheet.showAlert(contentView.getContext(), ChatActivity.this, resourceProvider); + RevenueSharingAdsInfoBottomSheet.showAlert(contentView.getContext(), ChatActivity.this, false, resourceProvider); } @Override @@ -38014,6 +38897,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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(); @@ -38417,6 +39303,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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)); @@ -38654,6 +39541,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (previousChat.topChatPanelView != null) { previousChat.topChatPanelView.setAlpha(1f - progress); } + if (previousChat.topChatPanelView2 != null) { + previousChat.topChatPanelView2.setAlpha(1f - progress); + } }); updateChatListViewTopPadding(); @@ -38701,6 +39591,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (previousChat.topChatPanelView != null) { previousChat.topChatPanelView.setAlpha(1f); } + if (previousChat.topChatPanelView2 != null) { + previousChat.topChatPanelView2.setAlpha(1f); + } } }); fragmentTransition.setDuration(300); @@ -39814,21 +40707,24 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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)); @@ -39980,7 +40876,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not }); } - 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(); @@ -40423,10 +41319,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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)); } @@ -40497,4 +41395,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not 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 3ea967ab9..4a81aa3d5 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 @@ package org.telegram.ui; 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; @@ -68,7 +69,6 @@ import org.telegram.tgnet.tl.TL_bots; 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; @@ -78,7 +78,6 @@ import org.telegram.ui.Cells.HeaderCell; 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; @@ -102,10 +101,14 @@ import org.telegram.ui.Components.SizeNotifierFrameLayout; 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 { @@ -146,6 +149,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; @@ -161,7 +165,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; @@ -1167,32 +1172,8 @@ public class ChatEditActivity extends BaseFragment implements ImageUpdater.Image 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); @@ -1222,7 +1203,7 @@ public class ChatEditActivity extends BaseFragment implements ImageUpdater.Image 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); @@ -1244,9 +1225,78 @@ public class ChatEditActivity extends BaseFragment implements ImageUpdater.Image 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)); @@ -1262,15 +1312,17 @@ public class ChatEditActivity extends BaseFragment implements ImageUpdater.Image 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)); @@ -1313,9 +1365,9 @@ public class ChatEditActivity extends BaseFragment implements ImageUpdater.Image } } - 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); } } @@ -1421,12 +1473,35 @@ public class ChatEditActivity extends BaseFragment implements ImageUpdater.Image } } 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.hasStars(userId)); + 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.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 323ac976f..0019ac46a 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.AndroidUtilities.dp; 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 class ChooseDownloadQualityLayout { 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 41f82c4e6..ee46f6b3f 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.content.Context; 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 class ChooseQualityLayout { 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 class ChooseQualityLayout { @Override public void draw(@NonNull Canvas canvas) { - final Rect bounds = getBounds(); + final float top_w = dp(5) * topText.isNotEmpty() + topText.getCurrentWidth(); + final float bottom_w = dp(5) * bottomText.isNotEmpty() + bottomText.getCurrentWidth(); - base.setBounds(bounds); + 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); + + 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 class ChooseQualityLayout { @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 e0381aac1..7f8c810d6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java @@ -100,7 +100,6 @@ import org.telegram.tgnet.ConnectionsManager; 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; @@ -1223,7 +1222,7 @@ public class AlertsCreator { final String scheme = url == null ? null : Uri.parse(url).getScheme(); SpoofedLinkChecker.SpoofedLinkInfo spoofedLinkInfo = SpoofedLinkChecker.isSpoofedLink(url, fragment, progress); if ((Browser.isInternalUrl(url, null) || !ask || "mailto".equalsIgnoreCase(scheme)) && !spoofedLinkInfo.isSpoofed) { - 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 a81116148..d41457a00 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 class AnimatedFileDrawable extends BitmapDrawable implements Animatable, } 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 class AnimatedFileDrawable extends BitmapDrawable implements Animatable, 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 class AnimatedFileDrawable extends BitmapDrawable implements Animatable, 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 class AnimatedFileDrawable extends BitmapDrawable implements Animatable, 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 90e980082..a6306b4cc 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 class AnimatedTextView extends View { 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 class AnimatedTextView extends View { 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 class AnimatedTextView extends View { } 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 @@ public class AnimatedTextView extends View { } } + public boolean centerY = true; + @Override public void draw(@NonNull Canvas canvas) { if (ellipsizeByGradient) { @@ -199,7 +212,7 @@ public class AnimatedTextView extends View { 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 class AnimatedTextView extends View { 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 class AnimatedTextView extends View { 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 class AnimatedTextView extends View { 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 class AnimatedTextView extends View { 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 @@ public class AnimatedTextView extends View { } } + 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 @@ public class AnimatedTextView extends View { 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 @@ public class AnimatedTextView extends View { 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 @@ public class AnimatedTextView extends View { 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 @@ public class AnimatedTextView extends View { } } 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 @@ public class AnimatedTextView extends View { } 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 class AnimatedTextView extends View { } 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 c98b50cf0..b03c1b9ce 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 05d09a599..48d95dca6 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 androidx.core.graphics.ColorUtils; 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.messenger.voip.VoIPService; 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 class AvatarsDrawable { 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 class AvatarsDrawable { } 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 651bef33b..369c6cb52 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BackButtonMenu.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BackButtonMenu.java @@ -80,6 +80,7 @@ public class BackButtonMenu { 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; @@ -108,6 +109,7 @@ public class BackButtonMenu { 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); @@ -120,6 +122,7 @@ public class BackButtonMenu { } 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; @@ -127,6 +130,7 @@ public class BackButtonMenu { imageView.setImage(ImageLocation.getForChat(chat, ImageLocation.TYPE_SMALL), "50_50", thumb, chat); titleView.setText(UserConfig.getChatTitleOverride(UserConfig.selectedAccount, chat)); } else if (user != null) { + hadDialogs = true; String name; if (user.photo != null && user.photo.strippedBitmap != null) { thumb = user.photo.strippedBitmap; @@ -208,6 +212,8 @@ public class BackButtonMenu { } } + 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 cc74baa2e..39c904925 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 class BackupImageView extends View { protected void onDetachedFromWindow() { super.onDetachedFromWindow(); attached = false; - imageReceiver.onDetachedFromWindow(); + if (applyAttach) imageReceiver.onDetachedFromWindow(); if (blurAllowed) { blurImageReceiver.onDetachedFromWindow(); } @@ -295,11 +295,13 @@ public class BackupImageView extends View { } } + 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 4fda8b778..83cc66a0e 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 class BlurringShader { } 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 c5580b76f..1176139e2 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 class Bulletin { } @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 d72644a51..7b74cf561 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.ApplicationLoader; 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 final class BulletinFactory { 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 final class BulletinFactory { 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 final class BulletinFactory { 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 final class BulletinFactory { 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 final class BulletinFactory { } } 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 final class BulletinFactory { 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 final class BulletinFactory { 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 final class BulletinFactory { } } 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 final class BulletinFactory { 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 final class BulletinFactory { 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 final class BulletinFactory { } } 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 final class BulletinFactory { } 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 final class BulletinFactory { 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 final class BulletinFactory { 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 dcd0c8425..bec647876 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java @@ -3257,21 +3257,22 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific 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(); } }); } @@ -3978,7 +3979,7 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific 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); @@ -3994,10 +3995,10 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific } }); }); - 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); @@ -4008,20 +4009,20 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific 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)) { @@ -9971,26 +9972,26 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific 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)) { @@ -10435,7 +10436,7 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific 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 4ca093bbb..8f0c58e12 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java @@ -883,6 +883,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N private TextView botMainButtonTextView; private float botMainButtonOffsetY; + private int editType; protected MessageObject editingMessageObject; private boolean buttonPressed; @@ -2039,6 +2040,8 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N 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)); @@ -2819,7 +2822,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N } }); 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; @@ -3133,10 +3136,12 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N 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) -> { @@ -3154,7 +3159,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N }, 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) { @@ -3169,7 +3174,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N } }); } - 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; @@ -3224,7 +3229,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N messageSendPreview.setItemOptions(options); messageSendPreview.setMessageObjects(messageObjects); - if (dialogId >= 0 && hasMessageToEffect) { + if (editingMessageObject == null && dialogId >= 0 && hasMessageToEffect) { messageSendPreview.allowEffectSelector(parentFragment); } @@ -3429,11 +3434,30 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N } } - 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; @@ -3442,6 +3466,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N allowOrder = true; } buttonsAdapter.notifyDataSetChanged(); + updateCountButton(0); } public MessageObject getEditingMessageObject() { @@ -4094,8 +4119,13 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N 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) { @@ -4419,7 +4449,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N 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; @@ -4431,6 +4461,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N } if (needMoreItem) { selectedMenuItem.setVisibility(View.VISIBLE); + selectedMenuItem.setClickable(true); } } else if (typeButtonsAvailable && frameLayout2.getTag() == null) { buttonsRecyclerView.setVisibility(View.VISIBLE); @@ -4496,6 +4527,8 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N } 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) { @@ -4565,6 +4598,15 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N } 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) { @@ -4575,6 +4617,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N if (menuShowed) { if (avatarPicker == 0 && !storyMediaPicker) { selectedMenuItem.setVisibility(View.VISIBLE); + selectedMenuItem.setClickable(true); } headerView.setVisibility(View.VISIBLE); } else { @@ -4585,6 +4628,8 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N 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) { @@ -4598,6 +4643,8 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N 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) { @@ -4694,17 +4741,36 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N 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; @@ -4845,6 +4911,10 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N if (optionsItem != null) { selectedTextView.setTranslationY(-AndroidUtilities.dp(8)); optionsItem.setVisibility(View.VISIBLE); + optionsItem.setClickable(true); + optionsItem.setAlpha(1f); + optionsItem.setScaleX(1f); + optionsItem.setScaleY(1f); } } @@ -5051,16 +5121,20 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N 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++; @@ -5358,8 +5432,10 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N 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 cc8db7dd8..3bbd568f8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java @@ -543,6 +543,13 @@ public class ChatAttachAlertPhotoLayout extends ChatAttachAlert.AttachAlertLayou 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() { @@ -1697,7 +1704,7 @@ public class ChatAttachAlertPhotoLayout extends ChatAttachAlert.AttachAlertLayou }); } - private void clearSelectedPhotos() { + public void clearSelectedPhotos() { spoilerItem.setText(LocaleController.getString(R.string.EnablePhotoSpoiler)); spoilerItem.setAnimatedIcon(R.raw.photo_spoiler); parentAlert.selectedMenuItem.showSubItem(compress); @@ -3362,7 +3369,7 @@ public class ChatAttachAlertPhotoLayout extends ChatAttachAlert.AttachAlertLayou 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 fb1fe22f6..11a3515a4 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 @@ public class ChatSearchTabs extends BlurredFrameLayout { } + 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 class ChatSearchTabs extends BlurredFrameLayout { 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 e1b69c43f..eecc2be5f 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 class CircularProgressDrawable extends Drawable { } 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 b96fbcbc2..d9f2ea2b0 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 class CreateGroupCallBottomSheet extends BottomSheetWithRecyclerListView 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 class CreateGroupCallBottomSheet extends BottomSheetWithRecyclerListView 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 @@ public class CreateGroupCallBottomSheet extends BottomSheetWithRecyclerListView 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 @@ public class CreateGroupCallBottomSheet extends BottomSheetWithRecyclerListView 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 class CreateGroupCallBottomSheet extends BottomSheetWithRecyclerListView @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 class CreateGroupCallBottomSheet extends BottomSheetWithRecyclerListView 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 class CreateGroupCallBottomSheet extends BottomSheetWithRecyclerListView 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 fdf6cd72d..4d717bfb0 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 static org.telegram.messenger.LocaleController.getString; 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 class DialogsBotsAdapter extends UniversalAdapter { 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 class DialogsBotsAdapter extends UniversalAdapter { 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 class DialogsBotsAdapter extends UniversalAdapter { 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 class DialogsBotsAdapter extends UniversalAdapter { 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 b7b04fa85..ab7b057e5 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 class EmojiPacksAlert extends BottomSheet implements NotificationCenter.N }); 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 class EmojiPacksAlert extends BottomSheet implements NotificationCenter.N 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 d57e1cc04..b22a680c1 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.graphics.ColorUtils; 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 class EmojiTabsStrip extends ScrollableHorizontalScrollView { } 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 EmojiTabsStrip extends ScrollableHorizontalScrollView { 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 class EmojiTabsStrip extends ScrollableHorizontalScrollView { 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 class EmojiTabsStrip extends ScrollableHorizontalScrollView { 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 class EmojiTabsStrip extends ScrollableHorizontalScrollView { 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 class EmojiTabsStrip extends ScrollableHorizontalScrollView { 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 class EmojiTabsStrip extends ScrollableHorizontalScrollView { } 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 class EmojiTabsStrip extends ScrollableHorizontalScrollView { public void setDrawable(Drawable drawable) { setAnimatedEmojiDocument(null); + setStickerThumb(null); imageView.setImageDrawable(drawable); } @@ -871,13 +863,26 @@ public class EmojiTabsStrip extends ScrollableHorizontalScrollView { 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 @@ public class EmojiTabsStrip extends ScrollableHorizontalScrollView { } public Drawable getDrawable() { - return imageView != null ? imageView.getDrawable() : null; + return imageView != null ? imageView.getImageReceiver().getImageDrawable() : null; } @Override @@ -1062,22 +1067,37 @@ public class EmojiTabsStrip extends ScrollableHorizontalScrollView { 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 @@ public class EmojiTabsStrip extends ScrollableHorizontalScrollView { 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 82bf73412..e2e9c1844 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 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific 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 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific 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 class EmojiView extends FrameLayout implements NotificationCenter.Notific 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 class EmojiView extends FrameLayout implements NotificationCenter.Notific 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 class EmojiView extends FrameLayout implements NotificationCenter.Notific // 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 02f73b7dc..79ea80aed 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.PorterDuffColorFilter; 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 android.widget.TextView; import androidx.annotation.IntDef; import androidx.annotation.Keep; +import androidx.annotation.NonNull; import org.telegram.messenger.AccountInstance; import org.telegram.messenger.AndroidUtilities; @@ -155,8 +159,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 @@ -167,7 +175,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent } ChatObject.Call call = chatActivity.getGroupCall(); if (call == null || !call.isScheduled()) { - timeLayout = null; + notifyButtonEnabled = false; scheduleRunnableScheduled = false; return; } @@ -179,8 +187,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent } 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(); } @@ -294,11 +301,13 @@ public class FragmentContextView extends FrameLayout implements NotificationCent } } + 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); @@ -320,18 +329,56 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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); @@ -346,7 +393,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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 -> { @@ -363,7 +410,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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) { @@ -420,9 +467,9 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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 @@ -461,11 +508,11 @@ public class FragmentContextView extends FrameLayout implements NotificationCent }; 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) { @@ -478,7 +525,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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 -> { @@ -492,12 +539,12 @@ public class FragmentContextView extends FrameLayout implements NotificationCent } 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; @@ -590,7 +637,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent }; 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); @@ -629,7 +676,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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)); @@ -759,7 +806,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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); @@ -793,7 +840,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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); @@ -868,10 +915,10 @@ public class FragmentContextView extends FrameLayout implements NotificationCent } } }; - 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); } @@ -879,7 +926,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent public void onPanTranslationUpdate(float y) { if (speedHintView != null) { - speedHintView.setExtraTranslationY(AndroidUtilities.dp(64 + 8) + y); + speedHintView.setExtraTranslationY(dp(64 + 8) + y); } } @@ -912,7 +959,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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))); } } @@ -1001,7 +1048,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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); @@ -1071,7 +1118,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent currentStyle = style; frameLayout.setWillNotDraw(currentStyle != STYLE_INACTIVE_GROUP_CALL); if (style != STYLE_INACTIVE_GROUP_CALL) { - timeLayout = null; + notifyButtonEnabled = false; } if (avatars != null) { @@ -1185,8 +1232,8 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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); @@ -1242,7 +1289,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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); @@ -1763,9 +1810,9 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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); @@ -1815,7 +1862,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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++) { @@ -1833,7 +1880,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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); @@ -1945,9 +1992,9 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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); @@ -2122,7 +2169,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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); @@ -2130,6 +2177,9 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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); @@ -2146,8 +2196,9 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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) { @@ -2183,9 +2234,9 @@ public class FragmentContextView extends FrameLayout implements NotificationCent } 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(); @@ -2295,8 +2346,8 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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); @@ -2341,14 +2392,14 @@ public class FragmentContextView extends FrameLayout implements NotificationCent // 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; } @@ -2400,10 +2451,10 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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 { @@ -2465,4 +2516,30 @@ public class FragmentContextView extends FrameLayout implements NotificationCent 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 83d9db260..54bf68480 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCreateSpan.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCreateSpan.java @@ -155,6 +155,11 @@ public class GroupCreateSpan extends View { 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); @@ -242,6 +247,8 @@ public class GroupCreateSpan extends View { } 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 000000000..0952a8191 --- /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 000000000..0009b9eb9 --- /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 974149a81..3c9c805a8 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.UserObject; import org.telegram.messenger.Utilities; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenuSubItem; import org.telegram.ui.ActionBar.ActionBarPopupWindow; import org.telegram.ui.ActionBar.BaseFragment; @@ -88,6 +89,7 @@ public class ItemOptions { private float translateX, translateY; private int dimAlpha; + private boolean drawScrim = true; private View dimView; private ViewTreeObserver.OnPreDrawListener preDrawListener; @@ -256,6 +258,35 @@ public class ItemOptions { 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 class ItemOptions { 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 class ItemOptions { 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 class ItemOptions { } public int getItemsCount() { + if (lastLayout == null && layout == null) + return 0; if (lastLayout == layout) { return lastLayout.getItemsCount(); } else { @@ -657,7 +718,9 @@ public class ItemOptions { 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 class ItemOptions { } 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 @@ public class ItemOptions { 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 66954282d..b0327f414 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActivity.java @@ -79,6 +79,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]; @@ -115,6 +116,7 @@ public class MediaActivity extends BaseFragment implements SharedMediaLayout.Sha 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) { @@ -544,6 +546,11 @@ public class MediaActivity extends BaseFragment implements SharedMediaLayout.Sha 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 731b09d03..0656fac1e 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 class MentionsContainerView extends BlurredFrameLayout implements Notific MentionsContainerView.this.onContextClick(result); } - }, resourcesProvider); + }, resourcesProvider, isStories()); paddedAdapter = new PaddedListAdapter(adapter); listView.setAdapter(paddedAdapter); @@ -611,6 +611,17 @@ public class MentionsContainerView extends BlurredFrameLayout implements Notific 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 class MentionsContainerView extends BlurredFrameLayout implements Notific } } + 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 970e845c4..a1c0f01c8 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 @@ import java.util.Date; 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 class MessagePrivateSeenView extends FrameLayout { 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 class MessagePrivateSeenView extends FrameLayout { } 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 @@ public class MessagePrivateSeenView extends FrameLayout { 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 70e5cd7c6..4c3edf152 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.webkit.WebView; 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 @@ public class PipVideoOverlay { } 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 class PipVideoOverlay { } 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 class PipVideoOverlay { 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 @@ public class PipVideoOverlay { 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 930f0de6a..4714b5fd1 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 @@ -462,7 +462,10 @@ public class UserSelectorBottomSheet extends BottomSheetWithRecyclerListView imp 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 35969bedc..29011a411 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 class GiveawayResultsMessageCell { 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 1849ab5e7..eaf1e6e43 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 class QuoteSpan implements LeadingMarginSpan { 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 f01a1c6f8..be023373e 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 939e4b203..23e1db253 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.Paint; 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 class RadialProgress2 { 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 class RadialProgress2 { miniMediaActionDrawable.setBackgroundDrawable(drawable); } + @Keep public void setBackgroundGradientDrawable(LinearGradient drawable) { mediaActionDrawable.setBackgroundGradientDrawable(drawable); miniMediaActionDrawable.setBackgroundGradientDrawable(drawable); @@ -162,6 +167,7 @@ public class RadialProgress2 { iconPressedColorKey = iconPressed; } + @Keep public void setColors(int circle, int circlePressed, int icon, int iconPressed) { circleColor = circle; circlePressedColor = circlePressed; @@ -232,6 +238,7 @@ public class RadialProgress2 { return miniMediaActionDrawable.getCurrentIcon(); } + @Keep public void setIcon(int icon, boolean ifSame, boolean animated) { if (ifSame && icon == mediaActionDrawable.getCurrentIcon()) { return; @@ -302,6 +309,7 @@ public class RadialProgress2 { 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 b1dfe856d..859d1450c 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.content.Context; 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.KeyEvent; 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.messenger.browser.Browser; 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 class ChatCustomReactionsEditActivity extends BaseFragment implements Not } }); - 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 class ChatCustomReactionsEditActivity extends BaseFragment implements Not 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 class ChatCustomReactionsEditActivity extends BaseFragment implements Not }, 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 @@ public class ChatCustomReactionsEditActivity extends BaseFragment implements Not 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 @@ public class ChatCustomReactionsEditActivity extends BaseFragment implements Not 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 class ChatCustomReactionsEditActivity extends BaseFragment implements Not 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 class ChatCustomReactionsEditActivity extends BaseFragment implements Not } 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 class ChatCustomReactionsEditActivity extends BaseFragment implements Not }).start(); } else { switchLayout.setVisibility(View.INVISIBLE); - actionButton.setVisibility(View.INVISIBLE); + actionButtonContainer.setVisibility(View.INVISIBLE); } } } @@ -771,12 +843,13 @@ public class ChatCustomReactionsEditActivity extends BaseFragment implements Not 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 @@ public class ChatCustomReactionsEditActivity extends BaseFragment implements Not 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 ced770f60..9cc1686ad 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 class ReactionsLayoutInBubble { Theme.ResourcesProvider resourcesProvider; private Integer scrimViewReaction; private float scrimProgress; + private boolean scrimDirection; int availableWidth; private int lastDrawnWidth; @@ -185,6 +186,7 @@ public class ReactionsLayoutInBubble { } } 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 class ReactionsLayoutInBubble { } 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 class ReactionsLayoutInBubble { 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 class ReactionsLayoutInBubble { 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 class ReactionsLayoutInBubble { 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 class ReactionsLayoutInBubble { 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 class ReactionsLayoutInBubble { 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 class ReactionsLayoutInBubble { public int lastDrawnTagDotColor; boolean isSelected; + public boolean inGroup; public boolean isTag; AvatarsDrawable avatarsDrawable; ArrayList users; @@ -765,10 +776,18 @@ public class ReactionsLayoutInBubble { 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 class ReactionsLayoutInBubble { 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 class ReactionsLayoutInBubble { 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 class ReactionsLayoutInBubble { 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 class ReactionsLayoutInBubble { 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 ebdd612dc..e851f23fd 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 @@ public class SearchTagsList extends BlurredFrameLayout implements NotificationCe @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 c42de0af0..f7bf4c267 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 class SearchViewPager extends ViewPagerFixed implements FilteredSearchVie 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 class SearchViewPager extends ViewPagerFixed implements FilteredSearchVie } }); + 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 @@ public class SearchViewPager extends ViewPagerFixed implements FilteredSearchVie } 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 @@ public class SearchViewPager extends ViewPagerFixed implements FilteredSearchVie } } + 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 class SearchViewPager extends ViewPagerFixed implements FilteredSearchVie 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 class SearchViewPager extends ViewPagerFixed implements FilteredSearchVie if (botsSearchLayoutManager != null) { botsSearchLayoutManager.scrollToPositionWithOffset(0, 0); } + if (hashtagSearchLayoutManager != null) { + hashtagSearchLayoutManager.scrollToPositionWithOffset(0, 0); + } viewsByType.clear(); } @@ -1062,6 +1205,7 @@ public class SearchViewPager extends ViewPagerFixed implements FilteredSearchVie 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 @@ public class SearchViewPager extends ViewPagerFixed implements FilteredSearchVie 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 class SearchViewPager extends ViewPagerFixed implements FilteredSearchVie 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 class SearchViewPager extends ViewPagerFixed implements FilteredSearchVie } public void showDownloads() { - setPosition(4); + setPosition((expandedPublicPosts ? 1 : 0) + 4); } public int getPositionForType(int initialSearchType) { @@ -1129,6 +1278,7 @@ public class SearchViewPager extends ViewPagerFixed implements FilteredSearchVie 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 class SearchViewPager extends ViewPagerFixed implements FilteredSearchVie 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 class SearchViewPager extends ViewPagerFixed implements FilteredSearchVie 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 class SearchViewPager extends ViewPagerFixed implements FilteredSearchVie 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 class SearchViewPager extends ViewPagerFixed implements FilteredSearchVie 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 000000000..2f1ebf9d9 --- /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 d84643a9d..df4916544 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java @@ -631,7 +631,9 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter 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; @@ -792,6 +794,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter } loadMediaCounts(); + if (parentFragment == null) return; NotificationCenter notificationCenter = parentFragment.getNotificationCenter(); notificationCenter.addObserver(this, NotificationCenter.mediaCountsDidLoad); notificationCenter.addObserver(this, NotificationCenter.mediaCountDidLoad); @@ -819,6 +822,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter return; } delegates.clear(); + if (parentFragment == null) return; NotificationCenter notificationCenter = parentFragment.getNotificationCenter(); notificationCenter.removeObserver(this, NotificationCenter.mediaCountsDidLoad); notificationCenter.removeObserver(this, NotificationCenter.mediaCountDidLoad); @@ -1112,6 +1116,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter } private void loadMediaCounts() { + if (parentFragment == null) return; parentFragment.getMediaDataController().getMediaCounts(dialogId, topicId, parentFragment.getClassGuid()); if (mergeDialogId != 0) { parentFragment.getMediaDataController().getMediaCounts(mergeDialogId, topicId, parentFragment.getClassGuid()); @@ -1119,6 +1124,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter } 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()); @@ -1573,135 +1579,137 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter 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; + 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()); } - 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); } - }); - 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()); + 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 (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(); - } - } - @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; - } - 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) { + @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; + } + switchToCurrentSelectedMode(false); } - 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); - } - } - @Override - public void onSearchPressed(EditText editText) { - super.onSearchPressed(editText); - if (savedMessagesContainer != null) { - savedMessagesContainer.chatActivity.hitSearch(); + @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); + } } - } - @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 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); + } photoVideoOptionsItem = new ImageView(context); photoVideoOptionsItem.setContentDescription(getString("AccDescrMoreOptions", R.string.AccDescrMoreOptions)); @@ -1956,10 +1964,12 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter } }); - 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; @@ -2287,7 +2297,12 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter } }; } 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); @@ -3072,6 +3087,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter 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; @@ -3748,7 +3764,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter 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 { @@ -3761,7 +3777,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter 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; @@ -4654,7 +4670,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter if (id < 0) { return false; } - if (!canShowSearchItem()) { + if (searchItem != null && !canShowSearchItem()) { searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); searchAlpha = 0; } else { @@ -4950,7 +4966,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter tabsAnimation = null; if (backAnimation) { mediaPages[1].setVisibility(View.GONE); - if (!canShowSearchItem()) { + if (searchItem != null && !canShowSearchItem()) { searchItem.setVisibility(isStoriesView() ? View.GONE : INVISIBLE); searchAlpha = 0; } else { @@ -4964,7 +4980,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter 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; @@ -5468,6 +5484,9 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter } 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(); @@ -5481,6 +5500,9 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter } 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(); @@ -6097,7 +6119,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter 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); @@ -6319,16 +6341,18 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter 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); @@ -6341,7 +6365,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter searchItemState = 0; searchAlpha = 1f; } - } else if (searchItem.getVisibility() == View.INVISIBLE) { + } else if (searchItem != null && searchItem.getVisibility() == View.INVISIBLE) { if (canShowSearchItem()) { searchItemState = 0; searchAlpha = 1; @@ -6363,12 +6387,12 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter 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) { @@ -6428,8 +6452,8 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter } 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); @@ -6448,7 +6472,9 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter actionBar.closeSearchField(); searchItemState = 0; searchAlpha = 0; - searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); + if (searchItem != null) { + searchItem.setVisibility(isStoriesView() ? View.GONE : View.INVISIBLE); + } updateOptionsSearch(); } } @@ -8773,14 +8799,21 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter 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; @@ -8792,7 +8825,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter 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) { @@ -9906,6 +9939,10 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter return null; } + public String getStoriesHashtagUsername() { + return null; + } + public TL_stories.MediaArea getStoriesArea() { return null; } @@ -10253,4 +10290,8 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter 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 2cc923841..a85548d02 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 class StickersAlert extends BottomSheet implements NotificationCenter.Not 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 class StickersAlert extends BottomSheet implements NotificationCenter.Not 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 class StickersAlert extends BottomSheet implements NotificationCenter.Not 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 class StickersAlert extends BottomSheet implements NotificationCenter.Not 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 class StickersAlert extends BottomSheet implements NotificationCenter.Not 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 class StickersAlert extends BottomSheet implements NotificationCenter.Not 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 class StickersAlert extends BottomSheet implements NotificationCenter.Not } 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 ae2637dd0..16894bf40 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 @@ public class StickersDialogs { } 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 627237279..36e5adc86 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 class TableView extends TableLayout { 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 class TableView extends TableLayout { 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 class TableView extends TableLayout { 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 class TableView extends TableLayout { } }, 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 2a8b8f86b..d95fa6d2b 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 class Text { 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 1f5714077..d873f2322 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.Cells.ChatMessageCell; 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 class ThanosEffect extends TextureView { 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 class ThanosEffect extends TextureView { if (whenDone != null) { Runnable runnable = whenDone; whenDone = null; - runnable.run(); + ensureRunOnUIThread(runnable); } return false; } @@ -171,7 +172,7 @@ public class ThanosEffect extends TextureView { if (whenDone != null) { Runnable runnable = whenDone; whenDone = null; - runnable.run(); + ensureRunOnUIThread(runnable); } } @@ -182,7 +183,8 @@ public class ThanosEffect extends TextureView { 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 class ThanosEffect extends TextureView { if (whenDone != null) { Runnable runnable = whenDone; whenDone = null; - runnable.run(); + ensureRunOnUIThread(runnable); } } @@ -237,7 +239,8 @@ public class ThanosEffect extends TextureView { 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 class ThanosEffect extends TextureView { } } + 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 class ThanosEffect extends TextureView { 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 @@ public class ThanosEffect extends TextureView { } public void kill() { - if (!alive) { + if (!alive.get()) { FileLog.d("ThanosEffect: kill failed, already dead"); return; } @@ -396,12 +408,12 @@ public class ThanosEffect extends TextureView { } 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 @@ public class ThanosEffect extends TextureView { 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 @@ public class ThanosEffect extends TextureView { 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 @@ public class ThanosEffect extends TextureView { 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 class ThanosEffect extends TextureView { 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 class ThanosEffect extends TextureView { } public void cancel(View view) { - if (!alive) { + if (!alive.get()) { return; } Handler handler = getHandler(); @@ -690,40 +700,38 @@ public class ThanosEffect extends TextureView { 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 class ThanosEffect extends TextureView { 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 c78a30224..ca6f211bc 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 class TrendingStickersLayout extends FrameLayout implements NotificationC } 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 642356879..8258c2a86 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 class UItem extends AdapterWithDiffUtils.Item { 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 4e465c232..8fe83f84f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java @@ -558,13 +558,15 @@ public class UndoView extends FrameLayout { } 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 35d1ace76..6627c6f4a 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.Handler; 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.PlaybackException; 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.AudioProcessor; 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 com.google.android.exoplayer2.video.VideoSize; 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.net.URLEncoder; 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis player.prepare(); audioPlayer.setMediaSource(mediaSource2, true); audioPlayer.prepare(); + activePlayers.add(playerId); } private MediaSource mediaSourceFromUri(Uri uri, String type) { @@ -354,7 +363,8 @@ public class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 @@ public class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 @@ public class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 @@ public class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis player.play(); } } + if (onQualityChangeListener != null) { + AndroidUtilities.runOnUIThread(onQualityChangeListener); + } + activePlayers.add(playerId); } } @@ -704,11 +830,11 @@ public class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 == null ? null : uri.document; + 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; + } + + 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis } } + 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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; + } + 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; } } - for (VideoUri v : uris) { - if (v.codec != null && preferableCodecs_2.contains(v.codec)) { - return v.document; - } + 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis } public void releasePlayer(boolean async) { + activePlayers.remove(playerId); if (player != null) { player.release(); player = null; @@ -999,11 +1314,17 @@ public class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis } } + 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis } } + 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis @Override public void flush(int sampleRateHz, int channelCount, int encoding) { - + } @@ -1559,4 +1925,13 @@ public class VideoPlayer implements Player.Listener, VideoListener, AnalyticsLis 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 6caca2f8c..3d8c5d886 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 class ViewPagerFixed extends FrameLayout { } } + @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 class ViewPagerFixed extends FrameLayout { 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 @@ public class ViewPagerFixed extends FrameLayout { 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 class ViewPagerFixed extends FrameLayout { 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 class ViewPagerFixed extends FrameLayout { 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 class ViewPagerFixed extends FrameLayout { 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 class ViewPagerFixed extends FrameLayout { 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 class ViewPagerFixed extends FrameLayout { 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 1007f9075..bfc33facd 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.webkit.WebView; 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 class WebPlayerView extends ViewGroup implements VideoPlayer.VideoPlayerD 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 1655d6fe8..54f277f7c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java @@ -196,6 +196,7 @@ import org.telegram.ui.Components.AvatarDrawable; 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; @@ -507,6 +508,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. private ActionBarMenuSubItem blockItem; private ActionBarMenuSubItem saveItem; + private IUpdateButton updateButton; private float additionalFloatingTranslation; private float additionalFloatingTranslation2; private float floatingButtonTranslation; @@ -592,11 +594,6 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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<>(); @@ -4194,12 +4191,12 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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); @@ -4705,7 +4702,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. updateDialogsHint(); }); contentView.addView(dialogsHintCell); - } else if (initialDialogsType == DIALOGS_TYPE_FORWARD) { + } else if (initialDialogsType == DIALOGS_TYPE_FORWARD || clickSelectsDialog()) { if (commentView != null) { commentView.onDestroy(); } @@ -5151,77 +5148,19 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } 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.pendingPtgAppUpdate.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(); @@ -5616,9 +5555,15 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } 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; @@ -5915,11 +5860,19 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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; } } @@ -5947,7 +5900,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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); @@ -5976,6 +5929,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. .show(); }); updateAuthHintCellVisibility(false); + StarsController.getInstance(currentAccount).loadStarGifts(); } else if ( folderId == 0 && MessagesController.getInstance(currentAccount).pendingSuggestions.contains("BIRTHDAY_SETUP") && @@ -6380,69 +6334,6 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } } - private void updateAppUpdateViews(boolean animated) { - if (updateLayout == null) { - return; - } - boolean show; - if (SharedConfig.isAppUpdateAvailable()) { - String fileName = FileLoader.getAttachFileName(SharedConfig.pendingPtgAppUpdate.document); - File path = getFileLoader().getPathToAttach(SharedConfig.pendingPtgAppUpdate.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) { @@ -8217,7 +8108,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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; @@ -8306,6 +8197,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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); @@ -8591,12 +8483,25 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } 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; @@ -11110,7 +11015,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. View databaseMigrationHint; private void updateMenuButton(boolean animated) { - if (menuDrawable == null || updateLayout == null) { + if (menuDrawable == null || updateButton == null) { return; } int type; @@ -11129,7 +11034,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. type = MenuDrawable.TYPE_DEFAULT; downloadProgress = 0.0f; } - updateAppUpdateViews(animated); + updateButton.update(animated); menuDrawable.setType(type, animated); menuDrawable.setUpdateDownloadProgress(downloadProgress, animated); } @@ -13399,6 +13304,28 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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) { @@ -13453,4 +13380,9 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. 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; + } + } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/EmojiAnimationsOverlay.java b/TMessagesProj/src/main/java/org/telegram/ui/EmojiAnimationsOverlay.java index 7c6339e67..fe1fd49f1 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.UserConfig; 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 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe 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 91b87170d..5657ec9b5 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 class FeaturedStickersActivity extends BaseFragment implements Notificati 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 21b8da25c..5bb9d0f86 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/FilterCreateActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/FilterCreateActivity.java @@ -2045,15 +2045,20 @@ public class FilterCreateActivity extends BaseFragment { 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) { @@ -2061,7 +2066,7 @@ public class FilterCreateActivity extends BaseFragment { } } else { bgPaint.setStyle(Paint.Style.FILL); - textPaint.setTextSize(dp(12)); + textPaint.setTextSize(dp(fontSize < 0 ? 12 : fontSize)); } } @@ -2117,7 +2122,7 @@ public class FilterCreateActivity extends BaseFragment { 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 e6dc441f2..7b9a08282 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 com.android.billingclient.api.QueryProductDetailsParams; 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 @@ public class GiftSheet extends BottomSheetWithRecyclerListView implements Notifi 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 class GiftSheet extends BottomSheetWithRecyclerListView implements Notifi 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 class GiftSheet extends BottomSheetWithRecyclerListView implements Notifi 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 class GiftSheet extends BottomSheetWithRecyclerListView implements Notifi 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 class GiftSheet extends BottomSheetWithRecyclerListView implements Notifi 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 8a88a88c3..a3c6edbdd 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 @@ public class ProfileGiftsContainer extends FrameLayout implements NotificationCe 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 class ProfileGiftsContainer extends FrameLayout implements NotificationCe 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 class ProfileGiftsContainer extends FrameLayout implements NotificationCe } } + 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 343e63d6a..1262caa88 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 org.telegram.ui.Stories.recorder.PreviewView; 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 class SendGiftSheet extends BottomSheetWithRecyclerListView { 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 efa9879fb..c13320626 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCallActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCallActivity.java @@ -1675,7 +1675,7 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter } 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); @@ -1684,7 +1684,7 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter } 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); @@ -4758,7 +4758,7 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter 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 4f33ea28d..0955cbb00 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java @@ -133,7 +133,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; @@ -147,7 +149,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 { @@ -164,10 +166,12 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen 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) { @@ -178,6 +182,14 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen spansContainer.removeSpan(selectedPremium); selectedPremium = null; } + if (miniapps && selectedMiniapps == null) { + selectedMiniapps = new GroupCreateSpan(getContext(), "miniapps", currentAccount); + 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) { @@ -364,6 +376,9 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen if (span == selectedPremium) { selectedPremium = null; } + if (span == selectedMiniapps) { + selectedMiniapps = null; + } allSpans.remove(span); span.setOnClickListener(null); @@ -464,6 +479,7 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen 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) { @@ -772,7 +788,7 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen }); if (toSelectIds != null) { - select(toSelectIds, toSelectPremium); + select(toSelectIds, toSelectPremium, toSelectMiniapps); } FlickerLoadingView flickerLoadingView = new FlickerLoadingView(context); @@ -815,6 +831,18 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen checkVisibleRows(); return; } + if (cell.currentMiniapps) { + if (selectedMiniapps == null) { + selectedMiniapps = new GroupCreateSpan(editText.getContext(), "miniapps", currentAccount); + 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) { @@ -1092,6 +1120,10 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen 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; } @@ -1216,7 +1248,7 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen } if (isAlwaysShare || isNeverShare) { if (delegate != null) { - delegate.didSelectUsers(selectedPremium != null, result); + delegate.didSelectUsers(selectedPremium != null, selectedMiniapps != null, result); } finishFragment(); } else { @@ -1332,6 +1364,7 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen private int userTypesHeaderRow; private int firstSectionRow; private int premiumRow; + private int miniappsRow; private int usersStartRow; private int inviteViaLink; private int noContactsStubRow; @@ -1443,6 +1476,7 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen userTypesHeaderRow = -1; firstSectionRow = -1; premiumRow = -1; + miniappsRow = -1; if (searching) { count = searchResult.size(); int localServerCount = searchAdapterHelper.getLocalServerSearch().size(); @@ -1458,6 +1492,9 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen if (allowPremium) { userTypesHeaderRow = firstSectionRow = count++; premiumRow = count++; + } else if (allowMiniapps) { + userTypesHeaderRow = firstSectionRow = count++; + miniappsRow = count++; } else { firstSectionRow = count; } @@ -1611,6 +1648,10 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen 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); } @@ -1657,7 +1698,7 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen 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 bc5fa6e6e..b45c51d39 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 @@ public class GroupStickersActivity extends BaseFragment implements NotificationC 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 000000000..f08c161b9 --- /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 2d1cc1160..1052a1ccb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java @@ -227,7 +227,6 @@ import java.util.HashMap; 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; @@ -237,6 +236,7 @@ import java.util.zip.ZipInputStream; 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; @@ -1036,29 +1036,29 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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 @@ -1214,7 +1214,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati }); - layersActionBarLayout = new ActionBarLayout(this, true); + layersActionBarLayout = new ActionBarLayout(this, false); layersActionBarLayout.setRemoveActionBarExtraHeight(true); layersActionBarLayout.setBackgroundView(shadowTablet); layersActionBarLayout.setUseAlphaAnimations(true); @@ -1433,6 +1433,9 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati } } } + for (BotWebViewSheet sheet : BotWebViewSheet.activeSheets) { + color = sheet.getNavigationBarColor(color); + } setNavigationBarColor(color, checkButtons); setLightNavigationBar(AndroidUtilities.computePerceivedBrightness(color) >= .721f); } @@ -1889,6 +1892,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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; @@ -2221,6 +2225,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati String attachMenuBotToOpen = null; String attachMenuBotChoose = null; boolean botCompact = false; + boolean botFullscreen = false; boolean openProfile = false; int storyId = 0; final String scheme = data.getScheme(); @@ -2465,6 +2470,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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"); @@ -2931,7 +2937,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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) { @@ -3957,9 +3963,9 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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); @@ -3969,7 +3975,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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) { @@ -3978,7 +3984,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati } 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); } @@ -4069,7 +4075,11 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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) { @@ -4160,7 +4170,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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; @@ -4181,7 +4191,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati } }), 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); @@ -4201,15 +4211,15 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati } }), 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; } @@ -4226,7 +4236,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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); @@ -4822,10 +4832,10 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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); @@ -5270,7 +5280,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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(); @@ -5282,7 +5292,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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(() -> { @@ -5292,15 +5302,18 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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); + if (botFullscreen) { + sheet.setFullscreen(true, false); + } sheet.setNeedsContext(false); sheet.setParentActivity(LaunchActivity.this); sheet.requestWebView(lastFragment, props); @@ -5308,22 +5321,22 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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(); - } - } +// } 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) { @@ -5352,36 +5365,37 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati } } } - 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; - } - } - 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); - } + 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; } } } + 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) { @@ -8722,6 +8736,12 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati 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 7f5497ad8..6c11cf01d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java @@ -220,8 +220,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, @@ -264,8 +263,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 {} @@ -542,19 +540,6 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No 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; @@ -1133,7 +1118,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No @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(); @@ -3270,7 +3255,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No } 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")) { @@ -5232,83 +5217,73 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No 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)); - } else { - needShowAlert(getString(R.string.RestorePasswordNoEmailTitle), error.text); - } + 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; } - }), 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(); - } - }); - } + 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)); + } 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(); + } + }); } @Override @@ -5414,10 +5389,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No 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); @@ -5451,16 +5423,9 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No 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 33bff6160..9b4381a20 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.ScrollView; 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 @@ public class PaymentFormActivity extends BaseFragment implements NotificationCen } 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 247235bd2..6bc382e7d 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.StaticLayout; 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; @@ -186,6 +187,8 @@ import org.telegram.messenger.browser.Browser; import org.telegram.messenger.camera.Size; import org.telegram.messenger.fakepasscode.FakePasscodeUtils; import org.telegram.messenger.partisan.Utils; +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; @@ -194,6 +197,7 @@ import org.telegram.messenger.fakepasscode.RemoveAfterReadingMessages; 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; @@ -259,6 +263,7 @@ import org.telegram.ui.Components.RadialProgressView; 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; @@ -809,20 +814,23 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -863,6 +871,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private int touchSlop; private VideoForwardDrawable videoForwardDrawable; + private SeekSpeedDrawable seekSpeedDrawable; private AnimatorSet currentListViewAnimation; private PhotoCropView photoCropView; @@ -927,7 +936,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat @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) { @@ -1036,7 +1045,38 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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)); @@ -1420,7 +1460,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -1990,26 +2030,28 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -2793,6 +2835,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat return true; } default void onReleasePlayerBeforeClose(int currentIndex) {}; + default long getDialogId() { return 0; } default boolean forceAllInGroup() { return false; @@ -3079,10 +3122,16 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); + } } } @@ -4631,6 +4680,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } }; containerView.setFocusable(false); + if (framesRewinder != null) { + framesRewinder.setParentView(containerView); + } containerView.setClipChildren(true); containerView.setClipToPadding(true); @@ -5286,7 +5338,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat }; masksAlert.show(); } else if (id == gallery_menu_pip) { - if (pipItem.getAlpha() != 1.0f) { + if (!menuItem.isSubItemVisible(gallery_menu_pip)) { return; } if (isEmbedVideo) { @@ -5459,6 +5511,13 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); } } @@ -5482,14 +5541,42 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); @@ -5499,28 +5586,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat }); 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(); @@ -5547,45 +5616,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); @@ -5596,6 +5627,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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); @@ -5856,6 +5895,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } }); + seekSpeedDrawable = new SeekSpeedDrawable(containerView::invalidate, false, false); + qualityChooseView = new QualityChooseView(parentActivity); qualityChooseView.setTranslationY(dp(120)); qualityChooseView.setVisibility(View.INVISIBLE); @@ -7603,65 +7644,223 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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 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"); - } else { - qualityIcon.text.setText(p + ""); - } - } else { - qualityIcon.text.setText(""); - } - } else { - qualityItem.setVisibility(View.GONE); - qualityIcon.text.setText(""); + 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 (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 { + 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); + } + 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 { + 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() { @@ -8474,7 +8673,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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) { @@ -9611,9 +9810,15 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -12687,7 +12892,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -13272,7 +13477,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); @@ -13296,7 +13501,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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)) { @@ -13407,19 +13613,22 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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) { @@ -13490,9 +13699,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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); @@ -13754,7 +13965,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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 { @@ -13770,7 +13982,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); } @@ -14174,22 +14387,27 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); } @@ -14491,6 +14709,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } 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() { @@ -14854,7 +15080,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -17134,10 +17360,12 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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(); @@ -17149,12 +17377,20 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -17266,6 +17502,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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; @@ -17643,7 +17882,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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) { @@ -17992,18 +18231,20 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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); @@ -18278,6 +18519,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } if (!usedSurfaceView || firstFrameRendered) { aspectRatioFrameLayout.draw(canvas); + if (framesRewinder != null) { + framesRewinder.draw(canvas, aspectRatioFrameLayout.getWidth(), aspectRatioFrameLayout.getHeight()); + } } if (usedSurfaceView && alpha != 1f) { if (surfaceBlackoutPaint == null) { @@ -18395,17 +18639,19 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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); @@ -18453,10 +18699,16 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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()) { @@ -18580,17 +18832,19 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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(); @@ -18726,7 +18980,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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<>(); @@ -18892,23 +19146,28 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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); } } @@ -18957,7 +19216,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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; } @@ -21026,7 +21285,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat 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) { @@ -21103,13 +21362,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } }); } - 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), () -> { @@ -21185,8 +21441,32 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } 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 c7abab3a7..a075330eb 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.SimpleTextView; 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 @@ public class PremiumFeatureCell extends FrameLayout { } 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 6009d569f..30bb7cfe3 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 class PremiumPreviewFragment extends BaseFragment implements Notification 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 class PremiumPreviewFragment extends BaseFragment implements Notification } 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 class PremiumPreviewFragment extends BaseFragment implements Notification 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 046b26c25..34a1ef572 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PrivacyControlActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PrivacyControlActivity.java @@ -101,9 +101,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; @@ -153,6 +155,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; @@ -480,6 +483,8 @@ public class PrivacyControlActivity extends BaseFragment implements Notification 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 @@ -636,16 +641,29 @@ public class PrivacyControlActivity extends BaseFragment implements Notification 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)); @@ -760,6 +778,8 @@ public class PrivacyControlActivity extends BaseFragment implements Notification 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(); } @@ -813,6 +833,13 @@ public class PrivacyControlActivity extends BaseFragment implements Notification 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); @@ -875,6 +902,9 @@ public class PrivacyControlActivity extends BaseFragment implements Notification } 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); @@ -883,6 +913,7 @@ public class PrivacyControlActivity extends BaseFragment implements Notification } 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); @@ -908,6 +939,10 @@ public class PrivacyControlActivity extends BaseFragment implements Notification currentMinus.addAll(FakePasscodeUtils.filterDialogIds(privacyValueDisallowUsers.users, currentAccount)); } 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) { @@ -925,15 +960,16 @@ public class PrivacyControlActivity extends BaseFragment implements Notification } } } - 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); @@ -989,6 +1025,9 @@ public class PrivacyControlActivity extends BaseFragment implements Notification 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; } @@ -1058,7 +1097,8 @@ public class PrivacyControlActivity extends BaseFragment implements Notification 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++; } @@ -1377,6 +1417,13 @@ public class PrivacyControlActivity extends BaseFragment implements Notification 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 { @@ -1390,6 +1437,13 @@ public class PrivacyControlActivity extends BaseFragment implements Notification } 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 { @@ -1511,6 +1565,8 @@ public class PrivacyControlActivity extends BaseFragment implements Notification 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) { @@ -1544,6 +1600,8 @@ public class PrivacyControlActivity extends BaseFragment implements Notification 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 { @@ -1595,6 +1653,8 @@ public class PrivacyControlActivity extends BaseFragment implements Notification 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 0f3e847bf..98e5187e2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java @@ -93,6 +93,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; @@ -246,7 +247,7 @@ public class PrivacySettingsActivity extends BaseFragment implements Notificatio 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) { @@ -387,6 +388,8 @@ public class PrivacySettingsActivity extends BaseFragment implements Notificatio 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) { @@ -700,6 +703,7 @@ public class PrivacySettingsActivity extends BaseFragment implements Notificatio noncontactsRow = -1; } birthdayRow = rowCount++; + giftsRow = rowCount++; bioRow = rowCount++; groupsRow = rowCount++; privacyShadowRow = rowCount++; @@ -804,18 +808,19 @@ public class PrivacySettingsActivity extends BaseFragment implements Notificatio } 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) { @@ -846,6 +851,10 @@ public class PrivacySettingsActivity extends BaseFragment implements Notificatio minus += FakePasscodeUtils.filterDialogIds(privacyValueDisallowUsers.users, accountInstance.getCurrentAccount()).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; @@ -857,17 +866,17 @@ public class PrivacySettingsActivity extends BaseFragment implements Notificatio } } 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) { @@ -885,31 +894,46 @@ public class PrivacySettingsActivity extends BaseFragment implements Notificatio } } 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"; } @@ -940,6 +964,7 @@ public class PrivacySettingsActivity extends BaseFragment implements Notificatio 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) || @@ -997,6 +1022,7 @@ public class PrivacySettingsActivity extends BaseFragment implements Notificatio 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) { @@ -1055,6 +1081,14 @@ public class PrivacySettingsActivity extends BaseFragment implements Notificatio 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 1eeb970a8..2d1a98382 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PrivacyUsersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PrivacyUsersActivity.java @@ -210,9 +210,11 @@ public class PrivacyUsersActivity extends BaseFragment implements NotificationCe } 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 f16405603..04f170a20 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java @@ -277,7 +277,10 @@ import org.telegram.ui.Stories.recorder.ButtonWithCounterView; 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; @@ -477,6 +480,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. private Paint whitePaint = new Paint(); private boolean isBot; + private BotLocation botLocation; + private BotBiometry botBiometry; private TLRPC.ChatFull chatInfo; private TLRPC.UserFull userInfo; @@ -619,6 +624,15 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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; @@ -632,7 +646,9 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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; @@ -1091,11 +1107,13 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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) { @@ -4036,15 +4054,15 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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); @@ -4119,6 +4137,37 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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(); @@ -4192,7 +4241,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. !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", @@ -4236,7 +4285,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. getMessagesStorage().clearSentMedia(); SharedConfig.setNoSoundHintShowed(false); SharedPreferences.Editor editor = MessagesController.getGlobalMainSettings().edit(); - editor.remove("archivehint").remove("proximityhint").remove("archivehint_l").remove("speedhint").remove("gifhint").remove("reminderhint").remove("soundHint").remove("themehint").remove("bganimationhint").remove("filterhint").remove("n_0").remove("storyprvhint").remove("storyhint").remove("storyhint2").remove("storydualhint").remove("storysvddualhint").remove("stories_camera").remove("dualcam").remove("dualmatrix").remove("dual_available").remove("archivehint").remove("askNotificationsAfter").remove("askNotificationsDuration").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; @@ -4470,6 +4519,9 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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) { @@ -7887,10 +7939,12 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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) { @@ -8788,6 +8842,11 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. testerSettingsRow = -1; versionRow = -1; botAppRow = -1; + botPermissionsHeader = -1; + botPermissionBiometry = -1; + botPermissionEmojiStatus = -1; + botPermissionLocation = -1; + botPermissionsDivider = -1; sendMessageRow = -1; reportRow = -1; @@ -8828,7 +8887,9 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. sharedMediaRow = -1; notificationsSimpleRow = -1; settingsRow = -1; - balanceRow = -1; + botStarsBalanceRow = -1; + botTonBalanceRow = -1; + channelBalanceRow = -1; balanceDividerRow = -1; unblockRow = -1; @@ -9008,6 +9069,28 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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++; @@ -9024,15 +9107,18 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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; } @@ -9092,28 +9178,28 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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++; @@ -9471,8 +9557,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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; @@ -9941,6 +10027,9 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. if (sharedMediaLayout != null && sharedMediaLayout.scrollSlidingTextTabStrip != null) { sharedMediaLayout.scrollSlidingTextTabStrip.updateColors(); } + if (sharedMediaLayout != null && sharedMediaLayout.giftsContainer != null) { + sharedMediaLayout.giftsContainer.updateColors(); + } writeButtonSetBackground(); updateEmojiStatusDrawableColor(); } @@ -10078,7 +10167,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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)); @@ -11388,6 +11478,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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; @@ -11630,9 +11722,9 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. } 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) { @@ -11653,10 +11745,9 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. } } 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) { @@ -11675,7 +11766,31 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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); @@ -11766,6 +11881,12 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. } 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; @@ -12101,7 +12222,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. @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 || position == chatIdRow) { return VIEW_TYPE_TEXT_DETAIL; @@ -12117,7 +12238,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. position == questionRow || position == devicesRow || position == filtersRow || position == stickersRow || position == faqRow || position == policyRow || position == sendLogsRow || position == sendLastLogsRow || position == clearLogsRow || position == sendLogcatRow || position == switchBackendRow || position == testerSettingsRow || 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; @@ -12129,7 +12250,8 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. position == secretSettingsSectionRow || position == settingsSectionRow || position == devicesSectionRow || position == helpSectionCell || position == setAvatarSectionRow || position == passwordSuggestionSectionRow || position == securitySuggestionSectionRow || 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) { @@ -13456,9 +13578,16 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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) { @@ -13866,7 +13995,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. 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 872a9c6d5..b47376c16 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 @@ public class ReportBottomSheet extends BottomSheet { 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 class ReportBottomSheet extends BottomSheet { 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 a27306060..f8bcb1467 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.LinearLayout; 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 class RevenueSharingAdsInfoBottomSheet extends BottomSheet { 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 class RevenueSharingAdsInfoBottomSheet extends BottomSheet { 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 class RevenueSharingAdsInfoBottomSheet extends BottomSheet { 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 class RevenueSharingAdsInfoBottomSheet extends BottomSheet { 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 800fa6429..6f105d3ed 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 @@ public class SelectAnimatedEmojiDialog extends FrameLayout implements Notificati 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 @@ public class SelectAnimatedEmojiDialog extends FrameLayout implements Notificati 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 class SelectAnimatedEmojiDialog extends FrameLayout implements Notificati 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 @@ public class SelectAnimatedEmojiDialog extends FrameLayout implements Notificati 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 @@ public class SelectAnimatedEmojiDialog extends FrameLayout implements Notificati 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 @@ public class SelectAnimatedEmojiDialog extends FrameLayout implements Notificati 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 000000000..1f29a1a8e --- /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 bf5ea1ba3..a354c3905 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.LinearLayout; 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.UserConfig; 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.BulletinFactory; 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.Stories.recorder.ButtonWithCounterView; 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 class BotStarsActivity extends BaseFragment implements NotificationCenter 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 class BotStarsActivity extends BaseFragment implements NotificationCenter @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 @@ public class BotStarsActivity extends BaseFragment implements NotificationCenter 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 @@ public class BotStarsActivity extends BaseFragment implements NotificationCenter 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 @@ public class BotStarsActivity extends BaseFragment implements NotificationCenter 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); + 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); + 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 @@ public class BotStarsActivity extends BaseFragment implements NotificationCenter 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 class BotStarsActivity extends BaseFragment implements NotificationCenter } 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 @@ public class BotStarsActivity extends BaseFragment implements NotificationCenter 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 class BotStarsActivity extends BaseFragment implements NotificationCenter } } + 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 c9ccb0a85..fd30ccaef 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 @@ public class BotStarsController { 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 TLRPC.TL_payments_starsRevenueStats getRevenueStats(long did, boolean force) { - Long lastLoaded = lastLoadedStats.get(did); - TLRPC.TL_payments_starsRevenueStats botStats = stats.get(did); + public void preloadStarsStats(long did) { + Long lastLoaded = lastLoadedBotStarsStats.get(did); + getStarsRevenueStats(did, lastLoaded == null || System.currentTimeMillis() - lastLoaded > 1000 * 30); + } + + 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 class BotStarsController { 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 class BotStarsController { 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 237cf8211..8dcb9c5d5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stars/StarsController.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stars/StarsController.java @@ -61,6 +61,7 @@ import org.telegram.ui.bots.BotWebViewSheet; import java.util.ArrayList; import java.util.Arrays; +import java.util.Calendar; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -580,7 +581,7 @@ public class StarsController { 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) { @@ -1153,6 +1154,7 @@ public class StarsController { 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(); @@ -1184,11 +1186,17 @@ public class StarsController { 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; @@ -1196,8 +1204,9 @@ public class StarsController { 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; @@ -1212,10 +1221,13 @@ public class StarsController { 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"); } @@ -1237,6 +1249,9 @@ public class StarsController { sheet.show(); } else { payAfterConfirmed(messageObject, inputInvoice, form, success -> { + if (subscription_period > 0) { + invalidateSubscriptions(true); + } if (whenDone != null) { whenDone.run(true); } @@ -1376,6 +1391,7 @@ public class StarsController { 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; @@ -1396,6 +1412,8 @@ public class StarsController { 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(); } @@ -1999,7 +2017,8 @@ public class StarsController { 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; @@ -2082,6 +2101,7 @@ public class StarsController { public int giftsHash; public long giftsRemoteTime; public final ArrayList gifts = new ArrayList<>(); + public final ArrayList birthdaySortedGifts = new ArrayList<>(); public void invalidateStarGifts() { giftsLoaded = false; @@ -2094,13 +2114,14 @@ public class StarsController { 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; @@ -2116,6 +2137,9 @@ public class StarsController { 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); @@ -2372,6 +2396,11 @@ public class StarsController { }); 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 5c288684f..b9ca19171 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.PorterDuffColorFilter; 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; @@ -83,6 +83,7 @@ import org.telegram.messenger.WebFile; import org.telegram.messenger.browser.Browser; import org.telegram.messenger.fakepasscode.FakePasscodeUtils; 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; @@ -1420,7 +1421,7 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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); @@ -1443,15 +1444,7 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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)) { @@ -1460,21 +1453,30 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi } else if (did >= 0) { TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(did); deleted = user == null; - if (transaction.photo == null) { - avatarDrawable.setInfo(currentAccount, user); - imageView.setForUserOrChat(user, avatarDrawable); - } + avatarDrawable.setInfo(currentAccount, user); + imageView.setForUserOrChat(user, avatarDrawable); username = UserObject.getUserName(user, currentAccount); } else { TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-did); deleted = chat == null; - if (transaction.photo == null) { - avatarDrawable.setInfo(currentAccount, chat); - imageView.setForUserOrChat(chat, avatarDrawable); - } + avatarDrawable.setInfo(currentAccount, chat); + imageView.setForUserOrChat(chat, avatarDrawable); username = chat == null ? "" : UserConfig.getChatTitleOverride(currentAccount, chat); } - 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); @@ -1485,17 +1487,17 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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); @@ -1528,15 +1530,24 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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")); @@ -1642,11 +1653,13 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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) { @@ -1668,8 +1681,15 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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); @@ -1696,20 +1716,53 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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); + } - long now = ConnectionsManager.getInstance(currentAccount).getCurrentTime(); - titleView.setText(chat.title); - if (subscription.canceled) { + 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); + } + + 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); @@ -1736,7 +1789,7 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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) ); } @@ -1793,6 +1846,7 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi String purchase, long stars, TLRPC.WebDocument photo, + int subscription_period, Utilities.Callback> whenConfirmed, Runnable whenDismissed ) { @@ -1873,10 +1927,25 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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); @@ -1899,9 +1968,32 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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); @@ -1955,16 +2047,34 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi } 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(); @@ -2363,10 +2473,13 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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, @@ -2407,7 +2520,11 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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(); @@ -2418,6 +2535,8 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi stringRes = "StarsNeededTextReactions"; } else if (type == TYPE_STAR_GIFT_BUY) { stringRes = "StarsNeededTextGift"; + } else if (type == TYPE_BIZ) { + stringRes = "StarsNeededBizText"; } else { stringRes = "StarsNeededText"; } @@ -2956,6 +3075,9 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi } 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); } @@ -3284,6 +3406,8 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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)); @@ -3358,8 +3482,8 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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(); @@ -3371,6 +3495,8 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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(); @@ -3405,6 +3531,8 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi lastFragment.presentFragment(ChatActivity.of(did)); } } + }, !UserObject.isDeleted(didUser) ? getString(R.string.Gift2ButtonSendGift) : null, () -> { + new GiftSheet(context, currentAccount, did, sheet[0]::dismiss).show(); }); } } @@ -3591,7 +3719,7 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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); @@ -3612,6 +3740,10 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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) { @@ -3684,18 +3816,51 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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)); @@ -3703,26 +3868,32 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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); @@ -3737,7 +3908,7 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi } 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); @@ -3749,10 +3920,17 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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() { @@ -3772,7 +3950,11 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi }, 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( @@ -3781,7 +3963,7 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi ); 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)); @@ -3802,19 +3984,19 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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; @@ -3832,11 +4014,21 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi })); }; 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)); @@ -3848,7 +4040,7 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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)); @@ -3868,7 +4060,7 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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); } })); }); @@ -3904,7 +4096,15 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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); } })); }); @@ -3920,45 +4120,55 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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); + } }); } } @@ -3966,6 +4176,9 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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(); @@ -4353,6 +4566,7 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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)); @@ -4364,7 +4578,9 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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) ), @@ -4375,10 +4591,20 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi )); 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(); @@ -4397,7 +4623,7 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi } 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(); }); } @@ -4419,12 +4645,13 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi } 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)) @@ -4483,9 +4710,45 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi .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); } @@ -4517,16 +4780,15 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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 -> { + button1.setOnClickListener(v -> { sheet[0].dismiss(); }); @@ -4537,60 +4799,24 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi } 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); - })); - }); - - 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(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) { @@ -4601,6 +4827,9 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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]; @@ -4622,15 +4851,15 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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) { @@ -4643,7 +4872,9 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi ((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)) ), @@ -4681,12 +4912,12 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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)) @@ -4745,10 +4976,52 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi .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); } @@ -4766,7 +5039,7 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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)); @@ -4786,60 +5059,24 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi } 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) { @@ -4916,7 +5153,7 @@ public class StarsIntroActivity extends GradientHeaderActivity implements Notifi 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 7e0ba1c8c..a2a2e9c17 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 class StatisticActivity extends BaseFragment implements NotificationCente 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 8f23224c0..4997883ba 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 @@ public class StickersActivity extends BaseFragment implements NotificationCenter 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 class StickersActivity extends BaseFragment implements NotificationCenter 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 ddacae5c7..5f913f658 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.CustomPopupMenu; 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 class PeerStoriesView extends SizeNotifierFrameLayout implements Notifica } 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 class PeerStoriesView extends SizeNotifierFrameLayout implements Notifica } 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 class PeerStoriesView extends SizeNotifierFrameLayout implements Notifica 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 000000000..ff4f7e86c --- /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 a4a3397c9..2efceb998 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesController.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesController.java @@ -58,6 +58,7 @@ import org.telegram.ui.Components.BulletinFactory; 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; @@ -2449,6 +2450,8 @@ public class StoriesController { } } + 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); @@ -2459,6 +2462,9 @@ public class StoriesController { if (archived != null) { archived.updateStories(storyItems); } + for (SearchStoriesList list : attachedSearchLists) { + list.updateStories(storyItems); + } } public void updateDeletedStoriesInLists(long dialogId, List storyItems) { @@ -2797,17 +2803,20 @@ public class StoriesController { 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; } @@ -2860,6 +2869,32 @@ public class StoriesController { 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 a171efff4..a949406f2 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 class CaptionContainerView extends FrameLayout { 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 class CaptionContainerView extends FrameLayout { 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 class CaptionContainerView extends FrameLayout { } } } + @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 a6911d506..f67ef08b2 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 class StoryRecorder implements NotificationCenter.NotificationCenterDeleg // 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 class StoryRecorder implements NotificationCenter.NotificationCenterDeleg 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 d0a7711ce..0d181f79b 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 class TopicCreateFragment extends BaseFragment { 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 d489bf59d..fbecd8eb7 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.DialogCell; 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 class TopicsFragment extends BaseFragment implements NotificationCenter.N 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 class TopicsFragment extends BaseFragment implements NotificationCenter.N 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 class TopicsFragment extends BaseFragment implements NotificationCenter.N if (topic == null) { return; } - if (opnendForSelect) { + if (openedForSelect) { if (onTopicSelectedListener != null) { onTopicSelectedListener.onTopicSelected(topic); } @@ -959,7 +962,7 @@ public class TopicsFragment extends BaseFragment implements NotificationCenter.N 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 @@ public class TopicsFragment extends BaseFragment implements NotificationCenter.N 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 @@ public class TopicsFragment extends BaseFragment implements NotificationCenter.N 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(getUserConfig().getChatTitleOverride(chatLocal)); Drawable rightIcon = null; @@ -2477,6 +2480,8 @@ public class TopicsFragment extends BaseFragment implements NotificationCenter.N 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 @@ public class TopicsFragment extends BaseFragment implements NotificationCenter.N 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 class TopicsFragment extends BaseFragment implements NotificationCenter.N } 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 class TopicsFragment extends BaseFragment implements NotificationCenter.N 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 000000000..a5a9c3360 --- /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 3b6a9306e..22ca68ea0 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.KeyGenParameterSpec; 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.security.SecureRandom; 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 class BotBiometry { 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 class BotBiometry { 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 class BotBiometry { 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 000000000..fa7169ad5 --- /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 000000000..f9996f328 --- /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 000000000..95514273a --- /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 000000000..4f474bb6b --- /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 b4afc181f..b7a757b0c 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.PorterDuff; 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.Gravity; 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.MessagesController; 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 class BotWebViewAttachedSheet implements NotificationCenter.NotificationC 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 class BotWebViewAttachedSheet implements NotificationCenter.NotificationC 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 class BotWebViewAttachedSheet implements NotificationCenter.NotificationC 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 class BotWebViewAttachedSheet implements NotificationCenter.NotificationC 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 class BotWebViewAttachedSheet implements NotificationCenter.NotificationC return true; } + @Override + public boolean hadDialog() { + return dialog != null; + } + Drawable verifiedDrawable; public void requestWebView(BaseFragment fragment, WebViewRequestProps props) { @@ -997,7 +1001,7 @@ public class BotWebViewAttachedSheet implements NotificationCenter.NotificationC 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 class BotWebViewAttachedSheet implements NotificationCenter.NotificationC 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 class BotWebViewAttachedSheet implements NotificationCenter.NotificationC 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 class BotWebViewAttachedSheet implements NotificationCenter.NotificationC 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 class BotWebViewAttachedSheet implements NotificationCenter.NotificationC 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 class BotWebViewAttachedSheet implements NotificationCenter.NotificationC 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 class BotWebViewAttachedSheet implements NotificationCenter.NotificationC 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 9722c03a2..e8a0336d6 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 class BotWebViewMenuContainer extends FrameLayout implements Notification 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 class BotWebViewMenuContainer extends FrameLayout implements Notification 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 class BotWebViewMenuContainer extends FrameLayout implements Notification 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 class BotWebViewMenuContainer extends FrameLayout implements Notification }); 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 class BotWebViewMenuContainer extends FrameLayout implements Notification 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 8a19f4d61..3f4c2b9df 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.content.ContextWrapper; 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.ViewGroup; 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.annotation.Nullable; 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.MessagesController; 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.BottomSheetTabs; 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi obj.actionBar.setAlpha(value); obj.updateLightStatusBar(); + obj.updateDownloadBulletinArrow(); }).setMultiplier(100f); private float actionBarTransitionProgress = 0f; private SpringAnimation springAnimation; @@ -160,10 +188,17 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi @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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi webViewContainer.destroyWebView(); NotificationCenter.getInstance(currentAccount).removeObserver(BotWebViewSheet.this, NotificationCenter.webViewResultSent); NotificationCenter.getGlobalInstance().removeObserver(BotWebViewSheet.this, NotificationCenter.didSetNewTheme); - BotWebViewSheet.super.dismiss(); + if (!superDismissed) { + BotWebViewSheet.super.dismiss(); + superDismissed = true; + } lastFragment.presentFragment(new INavigationLayout.NavigationParams(new ChatActivity(args1)).setRemoveLast(true)); } @@ -552,12 +798,20 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi @Override public void onSetupMainButton(boolean isVisible, boolean isActive, String text, int color, int textColor, boolean isProgressVisible, boolean hasShineEffect) { - setMainButton(BotWebViewAttachedSheet.MainButtonSettings.of(isVisible, isActive, text, color, textColor, isProgressVisible)); + botButtons.setMainState(BotButtons.ButtonState.of(isVisible, isActive, isProgressVisible, hasShineEffect, text, color, textColor), true); + if (fullscreen) { + updateFullscreenLayout(); + updateWindowFlags(); + } } @Override public void onSetupSecondaryButton(boolean isVisible, boolean isActive, String text, int color, int textColor, boolean isProgressVisible, boolean hasShineEffect, String position) { - + botButtons.setSecondaryState(BotButtons.ButtonState.of(isVisible, isActive, isProgressVisible, hasShineEffect, text, color, textColor, position), true); + if (fullscreen) { + updateFullscreenLayout(); + updateWindowFlags(); + } } @Override @@ -572,6 +826,26 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi public boolean isClipboardAvailable() { return MediaDataController.getInstance(currentAccount).botInAttachMenu(botId); } + + @Override + public String onFullscreenRequested(boolean fullscreen) { + if (BotWebViewSheet.this.fullscreen == fullscreen) { + if (BotWebViewSheet.this.fullscreen) + return "ALREADY_FULLSCREEN"; + return null; + } + setFullscreen(fullscreen, true); + return null; + } + + @Override + public BotSensors getBotSensors() { + if (sensors == null) { + sensors = new BotSensors(context, botId); + sensors.attachWebView(webViewContainer.getWebView()); + } + return sensors; + } }); linePaint.setStyle(Paint.Style.FILL_AND_STROKE); @@ -588,49 +862,56 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi swipeContainer.stickTo(-swipeContainer.getOffsetY() + swipeContainer.getTopActionBarOffsetY()); } }); - windowView.addView(swipeContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 24, 0, 0)); + windowView.addView(swipeContainer, swipeContainerLayoutParams = LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL)); + + botButtons = new BotButtons(getContext(), resourcesProvider) { + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + } - mainButton = new TextView(context) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - 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, heightMeasureSpec); } }; - mainButton.setVisibility(View.GONE); - mainButton.setAlpha(0f); - mainButton.setSingleLine(); - mainButton.setGravity(Gravity.CENTER); - mainButton.setTypeface(AndroidUtilities.bold()); - int padding = AndroidUtilities.dp(16); - mainButton.setPadding(padding, 0, padding, 0); - mainButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - mainButton.setOnClickListener(v -> 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } }); 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } 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 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } }); } + 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi public void onDetachedFromWindow() { super.onDetachedFromWindow(); + setAttached(false); + if (springAnimation != null) { springAnimation.cancel(); springAnimation = null; @@ -914,7 +1288,7 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } public boolean isFullSize() { - return fullsize == null ? defaultFullsize : fullsize; + return fullscreen || (fullsize == null ? defaultFullsize : fullsize); } @Override @@ -922,6 +1296,11 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi return false; } + @Override + public boolean hadDialog() { + return false; + } + Drawable verifiedDrawable; public void requestWebView(BaseFragment fragment, WebViewRequestProps props) { @@ -934,7 +1313,7 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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, currentAccount); try { TextPaint tp = new TextPaint(); @@ -942,7 +1321,7 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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.isVerified() || userInfo != null && userInfo.user != null && userInfo.user.isVerified()) { 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } }); } + if (fullscreenButtons != null) { + fullscreenButtons.setName(UserObject.getUserName(userbot), userbot != null && userbot.isVerified()); + } ActionBarMenu menu = actionBar.createMenu(); menu.removeAllViews(); @@ -990,20 +1372,30 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } else if (requestProps != null) { requestProps.applyResponse(response); - loadFromResponse(false); + loadFromResponse(); } })); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.webViewResultSent); @@ -1091,6 +1456,7 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } else if (requestProps != null) { requestProps.applyResponse(response); - loadFromResponse(false); + loadFromResponse(); } })); break; @@ -1121,6 +1487,7 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } else if (requestProps != null) { requestProps.applyResponse(response); - loadFromResponse(false); + loadFromResponse(); } })); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.webViewResultSent); @@ -1160,6 +1527,7 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } else if (requestProps != null) { requestProps.applyResponse(response2); - loadFromResponse(false); + loadFromResponse(); } }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); break; @@ -1190,6 +1558,7 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } else if (requestProps != null) { requestProps.applyResponse(response2); - loadFromResponse(false); + loadFromResponse(); } }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); break; @@ -1216,7 +1585,186 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } } - 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 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi @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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi }).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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } else { float progress = 1f; actionBarColor = to; - navBarColor = navBarTo; +// navBarColor = navBarTo; checkNavBarColor(); windowView.invalidate(); actionBar.setBackgroundColor(actionBarColor); @@ -1537,15 +2306,18 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 @@ public class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi 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 class BotWebViewSheet extends Dialog implements NotificationCenter.Notifi } - 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(); + 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(); } - }).start(); + }); + errorContainer.setBackgroundColor(backgroundPaint.getColor()); + AndroidUtilities.updateViewVisibilityAnimated(errorContainer, errorShown, 1f, false); } - radialProgressView.setProgressColor(s.textColor); - if (s.isProgressVisible != mainButtonProgressWasVisible) { - mainButtonProgressWasVisible = s.isProgressVisible; - radialProgressView.animate().cancel(); - if (s.isProgressVisible) { - radialProgressView.setAlpha(0f); - radialProgressView.setVisibility(View.VISIBLE); + 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++; } - 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(); + } 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 b3a5ae518..e8b1170c5 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.Context; 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 class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert } @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 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert lastSwipeTime = System.currentTimeMillis(); }); swipeContainer.setScrollEndListener(()-> webViewContainer.invalidateViewPortHeight(true)); - swipeContainer.setDelegate(() -> { + swipeContainer.setDelegate(byTap -> { if (!onCheckDismissByUser()) { swipeContainer.stickTo(0); } @@ -700,6 +703,36 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert 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 class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert } } + public boolean isFullSize() { + return fullsize; + } + private boolean allowFullSizeSwipe; public void setAllowFullSizeSwipe(boolean value) { allowFullSizeSwipe = value; @@ -788,7 +825,7 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert 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 class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert 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 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert 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 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert } } + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + } + public float getTopActionBarOffsetY() { return topActionBarOffsetY; } @@ -1033,6 +1080,7 @@ public class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert 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 class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert 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 class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert 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 class ChatAttachAlertBotWebViewLayout extends ChatAttachAlert.AttachAlert /** * 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 000000000..83d42083d --- /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 11edd8c71..377cab3b5 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 class WebViewRequestProps { 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 class WebViewRequestProps { 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 52f9e8e8e..d695ae8d2 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.content.pm.PackageManager; 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.Environment; 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.webkit.WebViewClient; 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.AlertDialog; 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.Premium.PremiumFeatureBottomSheet; 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.InputStream; 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 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific } } }; - 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 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific 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 abstract class BotWebViewContainer extends FrameLayout implements Notific 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 abstract class BotWebViewContainer extends FrameLayout implements Notific } } - 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 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific } 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 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific } } - 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 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific 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 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific } 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 abstract class BotWebViewContainer extends FrameLayout implements Notific 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 abstract class BotWebViewContainer extends FrameLayout implements Notific 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 abstract class BotWebViewContainer extends FrameLayout implements Notific 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 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific 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 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific 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 abstract class BotWebViewContainer extends FrameLayout implements Notific } 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 abstract class BotWebViewContainer extends FrameLayout implements Notific 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 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific 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 abstract class BotWebViewContainer extends FrameLayout implements Notific webView.reload(); } updateKeyboardFocusable(); + + if (sensors != null) { + sensors.stopAll(); + } }); } @@ -948,6 +1036,9 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific public void preserveWebView() { d("preserveWebView"); preserving = true; + if (bot) { + notifyEvent("visibility_changed", obj("is_visible", false)); + } } public void destroyWebView() { @@ -958,9 +1049,18 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific } 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 abstract class BotWebViewContainer extends FrameLayout implements Notific 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 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific 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 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific } } - private void onEventReceived(String eventType, String eventData) { + private void onEventReceived(BotWebViewProxy proxy, String eventType, String eventData) { if (!bot) { return; } @@ -1517,7 +1634,7 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific 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 abstract class BotWebViewContainer extends FrameLayout implements Notific 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 abstract class BotWebViewContainer extends FrameLayout implements Notific 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 abstract class BotWebViewContainer extends FrameLayout implements Notific 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 abstract class BotWebViewContainer extends FrameLayout implements Notific } } + 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 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific 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 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific return hex; } - public void onWebViewCreated() { + public void onWebViewCreated(MyWebView webView) { + + } + + public void onWebViewDestroyed(MyWebView webView) { } @@ -2306,16 +2778,25 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific 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 abstract class BotWebViewContainer extends FrameLayout implements Notific this.container = container; } + @Keep @JavascriptInterface public void post(String type, String data) { if (container == null) return; @@ -2341,6 +2823,7 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific }); } + @Keep @JavascriptInterface public void resolveShare(String json, byte[] file, String fileName, String fileMimeType) { AndroidUtilities.runOnUIThread(() -> { @@ -2451,6 +2934,10 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific 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 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific 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 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific 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 abstract class BotWebViewContainer extends FrameLayout implements Notific public String urlFallback = "about:blank"; public boolean dangerousUrl; + private BottomSheet currentSheet; + public DangerousWebWarning currentWarning; public boolean isPageLoaded() { return isPageLoaded; @@ -2722,7 +3223,9 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific 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 abstract class BotWebViewContainer extends FrameLayout implements Notific } } }); - builder.show(); + currentSheet = builder.show(); }); return true; @@ -2827,7 +3330,7 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific } } }); - builder.show(); + currentSheet = builder.show(); }); return true; @@ -3076,6 +3579,11 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific @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 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific 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 abstract class BotWebViewContainer extends FrameLayout implements Notific @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 abstract class BotWebViewContainer extends FrameLayout implements Notific @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 abstract class BotWebViewContainer extends FrameLayout implements Notific } 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 abstract class BotWebViewContainer extends FrameLayout implements Notific 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 36dcae69a..6baa06ef2 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 @@ package org.telegram.ui.web; import android.content.ContentResolver; import android.os.AsyncTask; +import android.os.Build; import android.webkit.MimeTypeMap; import android.webkit.URLUtil; @@ -23,13 +24,25 @@ import java.net.URL; 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 @@ public class HttpGetFileTask extends AsyncTask { 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 ce78dc60e..6bddb8789 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.WebView; 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 class WebInstantView { 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 7d792e9f2..5950bee2c 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.WebView; 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 @@ public class WebMetadataCache { 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 000000000..ace597a8c 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 000000000..f3b039412 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 000000000..28fb73399 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 000000000..b487ee55a 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 000000000..bc29a931e 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 000000000..0767f5883 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 000000000..9582667be 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 000000000..131ed2bf6 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 000000000..b89cdd4ab 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 000000000..eae78242f 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 000000000..6a633a37c 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 000000000..67ec316c6 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 000000000..5761f0f4c 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 000000000..dea97d352 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 000000000..3b5624e39 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 000000000..f7f2eb1b2 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 000000000..104d740e5 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 000000000..432f93102 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 000000000..d6783bcf7 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 000000000..b941b9940 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 000000000..53474c115 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 000000000..2e7d827ae 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 000000000..18f3e6cc3 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 000000000..b9a477f62 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 000000000..4389240a8 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 000000000..dbc598584 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 000000000..7b35eb12a 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 000000000..17cc5aa4b 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 000000000..bea53e7b0 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 000000000..e39f24e71 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 000000000..2bb13d55d 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 000000000..cc34fe79b 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 000000000..6c9cfc729 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 000000000..b470f69c5 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 000000000..12fef07a8 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 000000000..b6a435b20 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 000000000..558305d71 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 000000000..f2778e207 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 000000000..1c079c395 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 000000000..a8afa83df 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 000000000..b8680d7be 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 000000000..65fa4579b 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 000000000..570a69780 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 000000000..1ac236a05 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 000000000..4482b5f58 --- /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 000000000..5d698852d --- /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 000000000..9911aaee1 --- /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 1f156218f..b6cbf0f4f 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 68a009d1a..6b6c6e0c5 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. @@ -4036,6 +4067,7 @@ Live Stream Manage Live Streams Join + Notify Me %1$s member talking %1$s members talking speaking @@ -5972,6 +6004,8 @@ Edit Intro Edit Commands Balance + Toncoin + Stars Change Bot Settings Use @BotFather to manage this bot. Set public link @@ -7080,10 +7114,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 @@ -7382,14 +7422,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 @@ -7466,6 +7506,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. @@ -7489,7 +7532,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 @@ -7655,25 +7711,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. @@ -7706,6 +7776,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. @@ -7751,6 +7824,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**. @@ -7818,6 +7898,7 @@ Oops... Failed to load page. Failed to load **%s**. + Failed to load **%s**. Downloading... Downloading %s... Download file @@ -7873,6 +7954,8 @@ Examples %d user %d users + %d user + %d users Apps history Do you want to remove %s from your apps usage history? Bookmark @@ -7977,15 +8060,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 @@ -7993,6 +8084,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. @@ -8040,6 +8133,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**. @@ -8072,7 +8167,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. @@ -8096,23 +8193,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 @@ -8180,6 +8285,108 @@ 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 Fake Passcode 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 cd31b8a79..250dbf798 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 @@ public class ApplicationLoaderImpl extends ApplicationLoader { 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 1b7270392..6c5dc9476 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.Adapters.DrawerLayoutAdapter; import org.telegram.ui.Components.AlertsCreator; import org.telegram.ui.Components.BulletinFactory; 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 class ApplicationLoaderImpl extends ApplicationLoader { 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 000000000..7ab0ff5ba --- /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.pendingPtgAppUpdate.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.pendingPtgAppUpdate.document); + final File path = FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(SharedConfig.pendingPtgAppUpdate.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 6ddf20fbd..215d5e6b5 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=UCKJJtMyqB!9uGrAw6xu