update to 11.4.2 (5469)

This commit is contained in:
dkaraush 2024-11-20 18:15:52 +04:00
parent 9b78d437de
commit fb2e545101
295 changed files with 18975 additions and 3863 deletions

View file

@ -210,7 +210,7 @@ apply plugin: 'com.google.gms.google-services'
task checkVisibility {
doFirst {
def isPrivateBuild = project.gradle.startParameter.taskNames.find {
it.contains("HA_private") || it.contains("Debug") || it.contains("Release")
it.contains("HA_private") || it.contains("HA_hardcore") || it.contains("Debug") || it.contains("Release")
}
def isPublicAllowed = !project.hasProperty("IS_PRIVATE") || !project.property("IS_PRIVATE").toBoolean()
if (!isPrivateBuild && !isPublicAllowed) {

View file

@ -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

View file

@ -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},

Binary file not shown.

Binary file not shown.

View file

@ -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
@ -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
}

View file

@ -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

View file

@ -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 <errno.h>
#include <stddef.h>
#include <stdint.h>
#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 */

View file

@ -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 <stddef.h>
#include <stdint.h>
#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 */

View file

@ -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 <errno.h>
#include <stdarg.h>
#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 */

View file

@ -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 <stdint.h>
#include <stddef.h>
#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 */

View file

@ -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 <stddef.h>
#include <stdint.h>
#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 */

View file

@ -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 */

Binary file not shown.

Binary file not shown.

View file

@ -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;

View file

@ -576,6 +576,10 @@ int32_t ConnectionsManager::getCurrentTime() {
return (int32_t) (getCurrentTimeMillis() / 1000) + timeDifference;
}
int32_t ConnectionsManager::getCurrentPingTime() {
return (int32_t) currentPingTimeLive;
}
uint32_t ConnectionsManager::getCurrentDatacenterId() {
Datacenter *datacenter = getDatacenterWithId(DEFAULT_DATACENTER_ID);
return datacenter != nullptr ? datacenter->getDatacenterId() : INT_MAX;
@ -1159,6 +1163,8 @@ void ConnectionsManager::processServerResponse(TLObject *message, int64_t messag
if (!registeredForInternalPush) {
registerForInternalPushUpdates();
}
int32_t diff = getCurrentTimeMonotonicMillis() - sendingPushPingTime;
currentPingTimeLive = (diff + currentPingTimeLive) / 2;
if (LOGS_ENABLED) DEBUG_D("connection(%p, account%u, dc%u, type %d) received push ping", connection, instanceNum, datacenter->getDatacenterId(), connection->getConnectionType());
sendingPushPing = false;
} else {
@ -1191,7 +1197,7 @@ void ConnectionsManager::processServerResponse(TLObject *message, int64_t messag
}
} else if (response->ping_id == lastPingId) {
int32_t diff = (int32_t) (getCurrentTimeMonotonicMillis() / 1000) - pingTime;
currentPingTimeLive = ((getCurrentTimeMonotonicMillis() - pingTimeMs) + currentPingTimeLive) / 2;
if (abs(diff) < 10) {
currentPingTime = (diff + currentPingTime) / 2;
if (messageId != 0) {
@ -1755,9 +1761,11 @@ void ConnectionsManager::sendPing(Datacenter *datacenter, bool usePushConnection
request->ping_id = ++lastPingId;
if (usePushConnection) {
request->disconnect_delay = 60 * 7;
sendingPushPingTime = getCurrentTimeMonotonicMillis();
} else {
request->disconnect_delay = testBackend ? 10 : 35;
pingTime = (int32_t) (getCurrentTimeMonotonicMillis() / 1000);
pingTimeMs = getCurrentTimeMonotonicMillis();
pingTime = (int32_t) (pingTimeMs / 1000);
}
auto networkMessage = new NetworkMessage();
@ -2533,12 +2541,12 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
)) && !request->awaitingIntegrityCheck) {
if (!forceThisRequest && request->connectionToken > 0) {
if ((request->connectionType & ConnectionTypeGeneric || request->connectionType & ConnectionTypeTemp) && request->connectionToken == connection->getConnectionToken()) {
if (LOGS_ENABLED) DEBUG_D("request token is valid, not retrying %s (%p)", typeInfo.name(), request->rawRequest);
// if (LOGS_ENABLED) DEBUG_D("request token is valid, not retrying %s (%p)", typeInfo.name(), request->rawRequest);
iter++;
continue;
} else {
if (connection->getConnectionToken() != 0 && request->connectionToken == connection->getConnectionToken()) {
if (LOGS_ENABLED) DEBUG_D("request download token is valid, not retrying %s (%p)", typeInfo.name(), request->rawRequest);
// if (LOGS_ENABLED) DEBUG_D("request download token is valid, not retrying %s (%p)", typeInfo.name(), request->rawRequest);
iter++;
continue;
}

View file

@ -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<uint32_t, Datacenter *> datacenters;
std::map<int32_t, std::vector<std::int32_t>> 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;

View file

@ -24,6 +24,9 @@
-keep class com.google.android.exoplayer2.metadata.flac.PictureFrame { *; }
-keep class com.google.android.exoplayer2.decoder.SimpleDecoderOutputBuffer { *; }
-keep class org.telegram.ui.Stories.recorder.FfmpegAudioWaveformLoader { *; }
-keepclassmembers class ** {
@android.webkit.JavascriptInterface <methods>;
}
# https://developers.google.com/ml-kit/known-issues#android_issues
-keep class com.google.mlkit.nl.languageid.internal.LanguageIdentificationJni { *; }

View file

@ -590,6 +590,8 @@ e <intent-filter>
<receiver android:name=".voip.VoIPActionsReceiver" android:exported="false"/>
<receiver android:name=".ShortcutResultReceiver" android:exported="false"/>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"

View file

@ -184,3 +184,4 @@ chat_outSentClock=-594761027
chat_searchPanelText=-12609056
table_background=-526345
table_border=-2039584
dialogTopBackground=-13473128

View file

@ -21,6 +21,8 @@ profile_actionPressedBackground=-11756844
key_chat_messagePanelVoiceLock=-1
chat_secretChatStatusText=-8812137
switchTrack=-10652540
gift_ribbon=-12492673
gift_ribbon_soldout=-8500415
chat_attachAudioBackground=-626837
location_sendLocationBackground=-9919529
actionBarDefaultSubmenuBackground=-14075831
@ -124,6 +126,9 @@ chat_inReplyLine=-8796932
chat_inQuote=-8796932
chat_inAudioPerfomerSelectedText=-7490861
dialogBackground=-14602949
bot_loadingIcon=520093695
gift_ribbon=-12492673
gift_ribbon_soldout=-8500415
dialogLineProgressBackground=-13548718
chat_inReplyNameText=-8796932
chat_outAudioPerfomerSelectedText=-4268038
@ -447,3 +452,4 @@ iv_navigationBackground=-16777216
table_background=177390847
table_border=436207615
dialogCardShadow=1073741824
dialogTopBackground=-13473128

View file

@ -127,6 +127,9 @@ chat_inReplyLine=-8796932
chat_inQuote=-8796932
chat_inAudioPerfomerSelectedText=-7490861
dialogBackground=-14803426
bot_loadingIcon=520093695
gift_ribbon=-12492673
gift_ribbon_soldout=-8500415
dialogLineProgressBackground=-12303292
chat_inReplyNameText=-8796932
chat_outAudioPerfomerSelectedText=-7023626
@ -472,3 +475,4 @@ iv_navigationBackground=-16777216
table_background=177390847
table_border=436207615
dialogCardShadow=1073741824
dialogTopBackground=-13473128

View file

@ -127,7 +127,7 @@ public final class Format implements Bundleable {
* of format being constructed. See the {@link Format} Javadoc for information about which fields
* should be set for different types of format.
*/
public static final class Builder {
public static class Builder {
@Nullable private String id;
@Nullable private String label;
@ -138,6 +138,10 @@ public final class Format implements Bundleable {
private int peakBitrate;
@Nullable private String codecs;
@Nullable private Metadata metadata;
public boolean cached;
public long documentId;
public String documentFilename;
public int currentAccount;
// Container specific.
@ -207,6 +211,7 @@ public final class Format implements Bundleable {
tileCountVertical = NO_VALUE;
// Provided by the source.
cryptoType = C.CRYPTO_TYPE_NONE;
cached = false;
}
/**
@ -254,6 +259,10 @@ public final class Format implements Bundleable {
this.tileCountVertical = format.tileCountVertical;
// Provided by the source.
this.cryptoType = format.cryptoType;
this.cached = format.cached;
this.documentId = format.documentId;
this.currentAccount = format.currentAccount;
this.documentFilename = format.documentFilename;
}
/**
@ -281,6 +290,46 @@ public final class Format implements Bundleable {
return this;
}
@CanIgnoreReturnValue
public Builder setCached(boolean cached) {
this.cached = cached;
return this;
}
@CanIgnoreReturnValue
public Builder setDocumentId(String documentId) {
try {
this.documentId = Long.parseLong(documentId);
} catch (Exception e) {}
return this;
}
@CanIgnoreReturnValue
public Builder setDocumentId(long documentId) {
this.documentId = documentId;
return this;
}
@CanIgnoreReturnValue
public Builder setCurrentAccount(int currentAccount) {
this.currentAccount = currentAccount;
return this;
}
@CanIgnoreReturnValue
public Builder setCurrentAccount(String currentAccount) {
try {
this.currentAccount = Integer.parseInt(currentAccount);
} catch (Exception e) {}
return this;
}
@CanIgnoreReturnValue
public Builder setDocumentFilename(String filename) {
this.documentFilename = filename;
return this;
}
/**
* Sets {@link Format#label}. The default value is {@code null}.
*
@ -747,6 +796,11 @@ public final class Format implements Bundleable {
/** Metadata, or null if unknown or not applicable. */
@Nullable public final Metadata metadata;
public boolean cached;
public long documentId;
public int currentAccount;
public String documentFilename;
// Container specific.
/** The mime type of the container, or null if unknown or not applicable. */
@ -1030,6 +1084,10 @@ public final class Format implements Bundleable {
bitrate = peakBitrate != NO_VALUE ? peakBitrate : averageBitrate;
codecs = builder.codecs;
metadata = builder.metadata;
cached = builder.cached;
documentId = builder.documentId;
currentAccount = builder.currentAccount;
documentFilename = builder.documentFilename;
// Container specific.
containerMimeType = builder.containerMimeType;
// Sample specific.

View file

@ -48,6 +48,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
import org.telegram.messenger.FileLog;
/** An HLS {@link MediaChunk}. */
/* package */ final class HlsMediaChunk extends MediaChunk {
@ -482,6 +483,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
} finally {
nextLoadPosition = (int) (input.getPosition() - dataSpec.position);
}
} catch (Exception e) {
FileLog.e(e);
} finally {
DataSourceUtil.closeQuietly(dataSource);
}

View file

@ -1557,6 +1557,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
sampleMimeType = sampleFormat.sampleMimeType;
}
sampleFormat.cached = playlistFormat.cached;
sampleFormat.documentId = playlistFormat.documentId;
sampleFormat.currentAccount = playlistFormat.currentAccount;
sampleFormat.documentFilename = playlistFormat.documentFilename;
Format.Builder formatBuilder =
sampleFormat
.buildUpon()
@ -1567,7 +1572,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
.setRoleFlags(playlistFormat.roleFlags)
.setAverageBitrate(propagateBitrates ? playlistFormat.averageBitrate : Format.NO_VALUE)
.setPeakBitrate(propagateBitrates ? playlistFormat.peakBitrate : Format.NO_VALUE)
.setCodecs(codecs);
.setCodecs(codecs)
.setCurrentAccount(playlistFormat.currentAccount)
.setDocumentId(playlistFormat.documentId)
.setCached(playlistFormat.cached)
.setDocumentFilename(playlistFormat.documentFilename);
if (sampleTrackType == C.TRACK_TYPE_VIDEO) {
formatBuilder

View file

@ -137,6 +137,11 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
private static final Pattern REGEX_BANDWIDTH = Pattern.compile("[^-]BANDWIDTH=(\\d+)\\b");
private static final Pattern REGEX_CHANNELS = Pattern.compile("CHANNELS=\"(.+?)\"");
private static final Pattern REGEX_CODECS = Pattern.compile("CODECS=\"(.+?)\"");
private static final Pattern REGEX_MIME = Pattern.compile("MIME=\"(.+?)\"");
private static final Pattern REGEX_CACHED = Pattern.compile("CACHED=\"(.+?)\"");
private static final Pattern REGEX_DOC_ID = Pattern.compile("DOCID=\"(.+?)\"");
private static final Pattern REGEX_DOC_FILENAME = Pattern.compile("DOCFILENAME=\"(.+?)\"");
private static final Pattern REGEX_ACCOUNT = Pattern.compile("ACCOUNT=\"(.+?)\"");
private static final Pattern REGEX_RESOLUTION = Pattern.compile("RESOLUTION=(\\d+x\\d+)");
private static final Pattern REGEX_FRAME_RATE = Pattern.compile("FRAME-RATE=([\\d\\.]+)\\b");
private static final Pattern REGEX_TARGET_DURATION =
@ -373,6 +378,11 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
int peakBitrate = parseIntAttr(line, REGEX_BANDWIDTH);
int averageBitrate = parseOptionalIntAttr(line, REGEX_AVERAGE_BANDWIDTH, -1);
String codecs = parseOptionalStringAttr(line, REGEX_CODECS, variableDefinitions);
String mime = parseOptionalStringAttr(line, REGEX_MIME, variableDefinitions);
boolean cached = TextUtils.equals(parseOptionalStringAttr(line, REGEX_CACHED, variableDefinitions), "true");
String documentId = parseOptionalStringAttr(line, REGEX_DOC_ID, variableDefinitions);
String documentFilename = parseOptionalStringAttr(line, REGEX_DOC_FILENAME, variableDefinitions);
String currentAccount = parseOptionalStringAttr(line, REGEX_ACCOUNT, variableDefinitions);
String resolutionString =
parseOptionalStringAttr(line, REGEX_RESOLUTION, variableDefinitions);
int width;
@ -420,12 +430,17 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
.setId(variants.size())
.setContainerMimeType(MimeTypes.APPLICATION_M3U8)
.setCodecs(codecs)
.setSampleMimeType(mime)
.setAverageBitrate(averageBitrate)
.setPeakBitrate(peakBitrate)
.setWidth(width)
.setHeight(height)
.setFrameRate(frameRate)
.setRoleFlags(roleFlags)
.setCached(cached)
.setDocumentId(documentId)
.setDocumentFilename(documentFilename)
.setCurrentAccount(currentAccount)
.build();
Variant variant =
new Variant(

View file

@ -37,8 +37,11 @@ import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import org.checkerframework.checker.nullness.compatqual.NullableType;
import org.telegram.messenger.FileLoader;
import org.telegram.messenger.FileLog;
/**
* A bandwidth based adaptive {@link ExoTrackSelection}, whose selected track is updated to be the
@ -442,7 +445,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
// Make initial selection
if (reason == C.SELECTION_REASON_UNKNOWN) {
reason = C.SELECTION_REASON_INITIAL;
selectedIndex = determineIdealSelectedIndex(nowMs, chunkDurationUs);
selectedIndex = determineIdealSelectedIndex(0, nowMs, chunkDurationUs);
return;
}
@ -454,7 +457,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
previousSelectedIndex = formatIndexOfPreviousChunk;
previousReason = Iterables.getLast(queue).trackSelectionReason;
}
int newSelectedIndex = determineIdealSelectedIndex(nowMs, chunkDurationUs);
int newSelectedIndex = determineIdealSelectedIndex(1, nowMs, chunkDurationUs);
if (!isBlacklisted(previousSelectedIndex, nowMs)) {
// Revert back to the previous selection if conditions are not suitable for switching.
Format currentFormat = getFormat(previousSelectedIndex);
@ -469,7 +472,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
} else if (selectedFormat.bitrate < currentFormat.bitrate
&& bufferedDurationUs >= 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<Integer, Integer> formatsByResolution = new HashMap<>();
final ArrayList<Integer> formatIndices = new ArrayList<>();
for (int i = 0; i < length; i++) {
if (nowMs == Long.MIN_VALUE || !isBlacklisted(i, nowMs)) {
if (nowMs != Long.MIN_VALUE && isBlacklisted(i, nowMs)) continue;
final Format format = getFormat(i);
final int resolution = Math.max(format.width, format.height);
if (!formatsByResolution.containsKey(resolution)) {
formatsByResolution.put(resolution, i);
formatIndices.add(i);
} else {
final int existingFormatIndex = formatsByResolution.get(resolution);
final Format existingFormat = getFormat(existingFormatIndex);
if (existingFormat.cached && !format.cached) continue;
if (
!existingFormat.cached && format.cached ||
format.bitrate < existingFormat.bitrate
) {
formatsByResolution.put(resolution, i);
formatIndices.remove((Integer) existingFormatIndex);
formatIndices.add(i);
}
}
}
if (type == 0) {
for (int i : formatIndices) {
Format format = getFormat(i);
if (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;
}

View file

@ -33,7 +33,7 @@ import java.util.ArrayList;
*/
public abstract class BaseDataSource implements DataSource {
private final boolean isNetwork;
protected boolean isNetwork;
private final ArrayList<TransferListener> listeners;
private int listenerCount;

View file

@ -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));
}
/**

View file

@ -666,6 +666,7 @@ public class AndroidUtilities {
}
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(str);
if (index >= 0) {
if (onClick != null) {
spannableStringBuilder.setSpan(new ClickableSpan() {
@Override
public void updateDrawState(@NonNull TextPaint ds) {
@ -681,6 +682,15 @@ public class AndroidUtilities {
}
}
}, index, index + len, 0);
} else {
spannableStringBuilder.setSpan(new CharacterStyle() {
@Override
public void updateDrawState(@NonNull TextPaint ds) {
ds.setUnderlineText(false);
ds.setColor(color);
}
}, index, index + len, 0);
}
}
return spannableStringBuilder;
}
@ -3202,8 +3212,36 @@ public class AndroidUtilities {
return new SpannableStringBuilder(str);
}
public static SpannableStringBuilder replaceTags(SpannableStringBuilder stringBuilder) {
try {
int start;
int end;
ArrayList<Integer> bolds = new ArrayList<>();
while ((start = AndroidUtilities.charSequenceIndexOf(stringBuilder, "**")) != -1) {
stringBuilder.replace(start, start + 2, "");
end = AndroidUtilities.charSequenceIndexOf(stringBuilder, "**");
if (end >= 0) {
stringBuilder.replace(end, end + 2, "");
bolds.add(start);
bolds.add(end);
}
}
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(stringBuilder);
for (int a = 0; a < bolds.size() / 2; a++) {
spannableStringBuilder.setSpan(new TypefaceSpan(AndroidUtilities.bold()), bolds.get(a * 2), bolds.get(a * 2 + 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return spannableStringBuilder;
} catch (Exception e) {
FileLog.e(e);
}
return stringBuilder;
}
private static Pattern linksPattern;
public static SpannableStringBuilder replaceLinks(String str, Theme.ResourcesProvider resourcesProvider) {
return replaceLinks(str, resourcesProvider, null);
}
public static SpannableStringBuilder replaceLinks(String str, Theme.ResourcesProvider resourcesProvider, Runnable onLinkClick) {
if (linksPattern == null) {
linksPattern = Pattern.compile("\\[(.+?)\\]\\((.+?)\\)");
}
@ -3220,6 +3258,9 @@ public class AndroidUtilities {
spannable.setSpan(new ClickableSpan() {
@Override
public void onClick(@NonNull View widget) {
if (onLinkClick != null) {
onLinkClick.run();
}
Browser.openUrl(ApplicationLoader.applicationContext, url);
}
@Override

View file

@ -45,6 +45,7 @@ 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;
@ -288,6 +289,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();
@ -616,6 +618,10 @@ public class ApplicationLoader extends Application {
return null;
}
public IUpdateButton takeUpdateButton(Context context) {
return null;
}
public TLRPC.Update parseTLUpdate(int constructor) {
return null;
}

View file

@ -130,10 +130,14 @@ public class BillingController implements PurchasesUpdatedListener, BillingClien
if (isReady()) {
return;
}
try {
BillingUtilities.extractCurrencyExp(currencyExpMap);
if (!BuildVars.useInvoiceBilling()) {
billingClient.startConnection(this);
}
} catch (Exception e) {
FileLog.e(e);
}
}
private void switchToInvoice() {

View file

@ -285,6 +285,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);

View file

@ -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();
}
}
}

View file

@ -84,6 +84,7 @@ public class ContactsController extends BaseController {
private ArrayList<TLRPC.PrivacyRule> addedByPhonePrivacyRules;
private ArrayList<TLRPC.PrivacyRule> voiceMessagesRules;
private ArrayList<TLRPC.PrivacyRule> birthdayPrivacyRules;
private ArrayList<TLRPC.PrivacyRule> giftsPrivacyRules;
private TLRPC.TL_globalPrivacySettings globalPrivacySettings;
public final static int PRIVACY_RULES_TYPE_LASTSEEN = 0;
@ -98,8 +99,9 @@ public class ContactsController extends BaseController {
public final static int PRIVACY_RULES_TYPE_BIO = 9;
public final static int PRIVACY_RULES_TYPE_MESSAGES = 10;
public final static int PRIVACY_RULES_TYPE_BIRTHDAY = 11;
public final static int PRIVACY_RULES_TYPE_GIFTS = 12;
public final static int PRIVACY_RULES_TYPE_COUNT = 12;
public final static int PRIVACY_RULES_TYPE_COUNT = 13;
private class MyContentObserver extends ContentObserver {
@ -331,6 +333,7 @@ public class ContactsController extends BaseController {
profilePhotoPrivacyRules = null;
bioPrivacyRules = null;
birthdayPrivacyRules = null;
giftsPrivacyRules = null;
forwardsPrivacyRules = null;
phonePrivacyRules = null;
@ -2690,6 +2693,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;
@ -2725,6 +2731,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;
@ -2789,6 +2798,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:
@ -2824,6 +2835,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;

View file

@ -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 {
@ -161,13 +162,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<TLRPC.TL_username> 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<TLRPC.TL_username> 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;

View file

@ -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) {

View file

@ -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;
@ -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,6 +1341,11 @@ public class FileLoadOperation {
private void cancel(boolean deleteFiles) {
Utilities.stageQueue.postRunnable(() -> {
cancelOnStage(deleteFiles);
});
}
private void cancelOnStage(boolean deleteFiles) {
if (state != stateFinished && state != stateFailed) {
state = stateCancelling;
cancelRequests(() -> {
@ -1374,7 +1401,6 @@ public class FileLoadOperation {
}
}
}
});
}
private void cancelRequests(Runnable fullyCancelled) {

View file

@ -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;

View file

@ -171,12 +171,10 @@ public class FilePathDatabase {
}
return path;
}
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");
}
useQueue = false;
}
if (useQueue) {
CountDownLatch syncLatch = new CountDownLatch(1);
String[] res = new String[1];

View file

@ -52,7 +52,7 @@ public class FileStreamLoadOperation extends BaseDataSource implements FileLoadO
private static final ConcurrentHashMap<Long, Integer> 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,7 +284,8 @@ public class FileStreamLoadOperation extends BaseDataSource implements FileLoadO
return Uri.fromFile(file);
}
try {
String params = "?account=" + currentAccount +
String params =
"?account=" + currentAccount +
"&id=" + document.id +
"&hash=" + document.access_hash +
"&dc=" + document.dc_id +

View file

@ -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<String> 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();
@ -163,6 +207,17 @@ public class HashtagSearchController {
req.offset_peer = MessagesController.getInstance(currentAccount).getInputPeer(search.lastOffsetPeer);
}
request = req;
} else {
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;
@ -175,7 +230,8 @@ public class HashtagSearchController {
}
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<MessageObject> 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<MessageObject> messages = new ArrayList<>();
HashMap<MessageCompositeID, Integer> generatedIds = new HashMap<>();
public static class SearchResult {
public final ArrayList<MessageObject> messages = new ArrayList<>();
public final HashMap<MessageCompositeID, Integer> 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;

View file

@ -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;

View file

@ -2095,6 +2095,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;

View file

@ -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]++;

View file

@ -9,6 +9,7 @@
package org.telegram.messenger;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@ -1279,6 +1280,32 @@ public 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<String> loadingStickerSetsKeys = new HashSet<>();
public TLRPC.TL_messages_stickerSet getStickerSet(TLRPC.InputStickerSet inputStickerSet, boolean cacheOnly) {
return getStickerSet(inputStickerSet, null, cacheOnly, null);
}
@ -1311,11 +1338,15 @@ public 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);
}
@ -1324,8 +1355,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);
}
@ -1336,6 +1368,8 @@ public class MediaDataController extends BaseController {
getNotificationCenter().postNotificationName(NotificationCenter.groupStickersDidLoad, set.set.id, set);
}
});
} else {
loadingStickerSetsKeys.remove(key);
}
});
});
@ -1344,6 +1378,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);
}
@ -1352,8 +1387,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);
}
@ -1364,11 +1400,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);
}
@ -1387,6 +1426,8 @@ public class MediaDataController extends BaseController {
getNotificationCenter().postNotificationName(NotificationCenter.groupStickersDidLoad, set.set.id, set);
}
});
} else {
loadingStickerSetsKeys.remove(key);
}
return null;
}
@ -1527,26 +1568,27 @@ public class MediaDataController extends BaseController {
return set;
}
private final HashMap<TLRPC.InputStickerSet, ArrayList<Utilities.Callback2<Boolean, TLRPC.TL_messages_stickerSet>>> loadingStickerSets = new HashMap<>();
private final HashMap<String, ArrayList<Utilities.Callback2<Boolean, TLRPC.TL_messages_stickerSet>>> loadingStickerSets = new HashMap<>();
private void fetchStickerSetInternal(TLRPC.InputStickerSet inputStickerSet, Utilities.Callback2<Boolean, TLRPC.TL_messages_stickerSet> onDone) {
if (onDone == null) {
return;
}
ArrayList<Utilities.Callback2<Boolean, TLRPC.TL_messages_stickerSet>> loading = loadingStickerSets.get(inputStickerSet);
final String key = inputSetKey(inputStickerSet);
ArrayList<Utilities.Callback2<Boolean, TLRPC.TL_messages_stickerSet>> 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<Utilities.Callback2<Boolean, TLRPC.TL_messages_stickerSet>> loadingCallbacks = loadingStickerSets.get(inputStickerSet);
ArrayList<Utilities.Callback2<Boolean, TLRPC.TL_messages_stickerSet>> loadingCallbacks = loadingStickerSets.get(key);
if (loadingCallbacks != null) {
for (int i = 0; i < loadingCallbacks.size(); ++i) {
if (response != null) {
@ -1556,7 +1598,7 @@ public class MediaDataController extends BaseController {
}
}
}
loadingStickerSets.remove(inputStickerSet);
loadingStickerSets.remove(key);
});
});
}
@ -3786,6 +3828,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;
}
@ -3921,7 +3964,7 @@ public class MediaDataController extends BaseController {
if (messageObject.hasValidGroupId()) {
messageObject.isPrimaryGroupMessage = true;
}
messageObject.setQuery(finalQuery);
messageObject.setQuery(finalQuery, !isHashtag);
messageObjects.add(messageObject);
}
}
@ -5452,6 +5495,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);
@ -5491,10 +5535,18 @@ public class MediaDataController extends BaseController {
return shortcutIntent;
}
public final HashMap<String, Utilities.Callback<Boolean>> shortcutCallbacks = new HashMap<>();
public void installShortcut(long dialogId, int type) {
installShortcut(dialogId, type, null);
}
public void installShortcut(long dialogId, int type, Utilities.Callback<Boolean> callback) {
try {
Intent shortcutIntent = type == SHORTCUT_TYPE_USER_OR_CHAT ? createIntrnalShortcutIntent(dialogId) : createIntrnalAttachedBotShortcutIntent(dialogId);
if (shortcutIntent == null) {
if (callback != null) {
callback.run(false);
}
return;
}
TLRPC.User user = null;
@ -5503,6 +5555,9 @@ public 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);
@ -5511,9 +5566,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;
}
@ -5624,7 +5685,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) {
@ -5736,12 +5811,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 ----------------
@ -7668,10 +7738,14 @@ public class MediaDataController extends BaseController {
}
threads.put(threadId, replyToMessage);
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))) {

View file

@ -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

View file

@ -196,6 +196,7 @@ public class MessageObject {
public int audioPlayerDuration;
public double attributeDuration;
public boolean isDateObject;
public boolean isVideoConversionObject;
public TLObject photoThumbsObject;
public TLObject photoThumbsObject2;
public ArrayList<TLRPC.PhotoSize> photoThumbs;
@ -310,6 +311,7 @@ public class MessageObject {
public CharSequence vCardData;
public ArrayList<String> highlightedWords;
public boolean messageTrimmedToHighlightCut = true;
public CharSequence messageTrimmedToHighlight;
public int parentWidth;
@ -476,6 +478,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);
}
@ -2624,19 +2665,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);
@ -5843,6 +5895,9 @@ public class MessageObject {
}
public String getFileName() {
if (getDocument() != null) {
return getFileName(getDocument());
}
return getFileName(messageOwner);
}
@ -6160,7 +6215,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);
}
@ -6314,7 +6369,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() {
@ -7868,7 +7923,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 {
@ -7882,10 +7937,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;
}
@ -8136,6 +8187,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;
}
@ -8145,7 +8197,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() {
@ -8156,10 +8212,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) {
@ -8206,6 +8258,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);
}
@ -8338,6 +8395,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;
@ -8442,7 +8500,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() {
@ -8677,6 +8735,9 @@ public class MessageObject {
if (emojiAnimatedSticker != null) {
return emojiAnimatedSticker;
}
if (hasVideoQualities() && highestQuality != null) {
return highestQuality.document;
}
return getDocument(messageOwner);
}
@ -9619,6 +9680,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;
}
@ -9651,6 +9714,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) {
@ -9966,37 +10030,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<String> foundWords = new ArrayList<>();
query = query.trim().toLowerCase();
String[] queryWord = query.split("\\P{L}+");
String[] queryWord = query.split("[^\\p{L}#$]+");
ArrayList<String> searchForWords = new ArrayList<>();
if (messageOwner.reply_to != null && !TextUtils.isEmpty(messageOwner.reply_to.quote_text)) {
String message = messageOwner.reply_to.quote_text.trim().toLowerCase();
if (message.contains(query) && !foundWords.contains(query)) {
foundWords.add(query);
handleFoundWords(foundWords, queryWord, true);
handleFoundWords(foundWords, queryWord, true, cut);
return;
}
String[] words = message.split("\\P{L}+");
String[] words = message.split("[^\\p{L}#$]+");
searchForWords.addAll(Arrays.asList(words));
}
if (!TextUtils.isEmpty(messageOwner.message)) {
String message = messageOwner.message.trim().toLowerCase();
if (message.contains(query) && !foundWords.contains(query)) {
foundWords.add(query);
handleFoundWords(foundWords, queryWord, false);
handleFoundWords(foundWords, queryWord, false, cut);
return;
}
String[] words = message.split("\\P{L}+");
String[] words = message.split("[^\\p{L}#$]+");
searchForWords.addAll(Arrays.asList(words));
}
if (getDocument() != null) {
@ -10004,7 +10073,7 @@ public 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));
}
@ -10019,7 +10088,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));
}
}
@ -10030,7 +10099,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++) {
@ -10065,10 +10134,13 @@ public class MessageObject {
}
}
}
handleFoundWords(foundWords, queryWord, false);
handleFoundWords(foundWords, queryWord, false, cut);
}
private void handleFoundWords(ArrayList<String> foundWords, String[] queryWord, boolean inQuote) {
handleFoundWords(foundWords, queryWord, inQuote, true);
}
private void handleFoundWords(ArrayList<String> foundWords, String[] queryWord, boolean inQuote, boolean cut) {
if (!foundWords.isEmpty()) {
boolean foundExactly = false;
for (int i = 0; i < foundWords.size(); i++) {
@ -10128,11 +10200,12 @@ 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;
}
}
}
@ -11031,11 +11104,21 @@ public class MessageObject {
}
private Boolean videoQualitiesCached;
public ArrayList<VideoPlayer.Quality> videoQualities;
public TLRPC.Document qualityToSave;
public VideoPlayer.VideoUri highestQuality, thumbQuality;
public boolean hasVideoQualities() {
if (videoQualitiesCached == null) {
try {
videoQualitiesCached = messageOwner != null && VideoPlayer.hasQualities(currentAccount, messageOwner.media);
if (messageOwner == null || messageOwner.media == null || messageOwner.media.document == null || messageOwner.media.alt_documents.isEmpty()) {
return videoQualitiesCached = false;
}
videoQualities = VideoPlayer.getQualities(currentAccount, messageOwner != null ? messageOwner.media : null);
videoQualitiesCached = videoQualities != null && videoQualities.size() > 1;
highestQuality = VideoPlayer.getQualityForPlayer(videoQualities);
thumbQuality = VideoPlayer.getQualityForThumb(videoQualities);
} catch (Exception e) {
FileLog.e(e);
videoQualitiesCached = false;
@ -11047,4 +11130,21 @@ public 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);
}
}
}
}

View file

@ -697,6 +697,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;
@ -965,7 +966,6 @@ public class MessagesController extends BaseController implements NotificationCe
public Integer posts_between;
public long loadTime;
public boolean loading;
public boolean faked;
}
private class SendAsPeersInfo {
@ -1553,6 +1553,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);
@ -2381,7 +2382,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) {
@ -3738,6 +3739,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;
@ -4929,7 +4941,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<TLRPC.JSONValue> value) {
@ -8320,6 +8333,10 @@ public class MessagesController extends BaseController implements NotificationCe
}
public void deleteMessages(ArrayList<Integer> messages, ArrayList<Long> randoms, TLRPC.EncryptedChat encryptedChat, long dialogId, boolean forAll, int mode, boolean cacheOnly, long taskId, TLObject taskRequest, int topicId) {
deleteMessages(messages, randoms, encryptedChat, dialogId, forAll, mode, cacheOnly, taskId, taskRequest, topicId, false, 0);
}
public void deleteMessages(ArrayList<Integer> messages, ArrayList<Long> randoms, TLRPC.EncryptedChat encryptedChat, long dialogId, boolean forAll, int mode, boolean cacheOnly, long taskId, TLObject taskRequest, int topicId, boolean movedToScheduled, int movedToScheduledMessageId) {
final boolean scheduled = mode == ChatActivity.MODE_SCHEDULED;
final boolean quickReplies = mode == ChatActivity.MODE_QUICK_REPLIES;
if ((messages == null || messages.isEmpty()) && taskId == 0) {
@ -8365,7 +8382,7 @@ public class MessagesController extends BaseController implements NotificationCe
getMessagesStorage().markMessagesAsDeleted(dialogId, messages, true, forAll, 0, topicId);
getMessagesStorage().updateDialogsWithDeletedMessages(dialogId, channelId, messages, null, true);
}
getNotificationCenter().postNotificationName(NotificationCenter.messagesDeleted, messages, channelId, scheduled);
getNotificationCenter().postNotificationName(NotificationCenter.messagesDeleted, messages, channelId, scheduled, false, movedToScheduled, movedToScheduledMessageId);
} else {
if (taskRequest instanceof TLRPC.TL_channels_deleteMessages) {
channelId = ((TLRPC.TL_channels_deleteMessages) taskRequest).channel.channel_id;
@ -10816,6 +10833,14 @@ public 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;
@ -16648,6 +16673,7 @@ public class MessagesController extends BaseController implements NotificationCe
LongSparseArray<ArrayList<Integer>> deletedMessages = null;
LongSparseArray<ArrayList<Integer>> deletedQuickReplyMessages = null;
LongSparseArray<ArrayList<Integer>> scheduledDeletedMessages = null;
LongSparseArray<ArrayList<Integer>> scheduledDeletedMessagesSent = null;
LongSparseArray<ArrayList<Long>> groupSpeakingActions = null;
LongSparseIntArray importingActions = null;
LongSparseIntArray clearHistoryMessages = null;
@ -16994,12 +17020,24 @@ public class MessagesController extends BaseController implements NotificationCe
scheduledDeletedMessages = new LongSparseArray<>();
}
long id = MessageObject.getPeerId(update.peer);
ArrayList<Integer> arrayList = scheduledDeletedMessages.get(MessageObject.getPeerId(update.peer));
ArrayList<Integer> 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<Integer> arrayList2 = scheduledDeletedMessagesSent.get(id);
if (arrayList2 == null) {
arrayList2 = new ArrayList<>();
scheduledDeletedMessagesSent.put(id, arrayList2);
}
arrayList2.addAll(update.sent_messages);
}
} else if (baseUpdate instanceof TLRPC.TL_updateUserTyping || baseUpdate instanceof TLRPC.TL_updateChatUserTyping || baseUpdate instanceof TLRPC.TL_updateChannelUserTyping) {
long userId;
long chatId;
@ -17891,6 +17929,10 @@ public 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);
@ -18483,7 +18525,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) {
@ -18885,6 +18927,7 @@ public class MessagesController extends BaseController implements NotificationCe
LongSparseArray<ArrayList<Integer>> deletedMessagesFinal = deletedMessages;
LongSparseArray<ArrayList<Integer>> deletedQuickRepliesMessagesFinal = deletedQuickReplyMessages;
LongSparseArray<ArrayList<Integer>> scheduledDeletedMessagesFinal = scheduledDeletedMessages;
LongSparseArray<ArrayList<Integer>> scheduledDeletedMessagesSentFinal = scheduledDeletedMessagesSent;
LongSparseIntArray clearHistoryMessagesFinal = clearHistoryMessages;
getMessagesStorage().getStorageQueue().postRunnable(() -> AndroidUtilities.runOnUIThread(() -> {
int updateMask = 0;
@ -19023,8 +19066,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<Integer> sentMessageIds = scheduledDeletedMessagesSentFinal != null ? scheduledDeletedMessagesSentFinal.get(key) : null;
getNotificationCenter().postNotificationName(NotificationCenter.messagesDeleted, arrayList, DialogObject.isChatDialog(key) && ChatObject.isChannel(getChat(-key)) ? -key : 0, true, false, false, 0, sentMessageIds);
}
}
if (clearHistoryMessagesFinal != null) {
@ -19315,36 +19358,19 @@ public 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<MessageObject> result;
Integer posts_between;
@ -21918,6 +21944,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 };
@ -21939,50 +21968,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));
}

View file

@ -44,6 +44,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;
@ -11927,12 +11928,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<VideoPlayer.Quality> qualities = VideoPlayer.getQualities(currentAccount, message.media);
if (qualities != null) {
VideoPlayer.VideoUri v = VideoPlayer.getQualityForThumb(qualities);
if (v != null) {
document = v.document;
}
}
TLRPC.Photo photo = MessageObject.getPhoto(message);
if (MessageObject.isVoiceMessage(message)) {
id = document.id;
@ -16097,6 +16106,32 @@ 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;

View file

@ -146,24 +146,18 @@ public class NotificationCenter {
public static final int quickRepliesDeleted = totalEvents++;
public static final int bookmarkAdded = totalEvents++;
public static final int starReactionAnonymousUpdate = totalEvents++;
public static final int businessLinksUpdated = totalEvents++;
public static final int businessLinkCreated = totalEvents++;
public static final int needDeleteBusinessLink = totalEvents++;
public static final int messageTranslated = totalEvents++;
public static final int messageTranslating = totalEvents++;
public static final int dialogIsTranslatable = totalEvents++;
public static final int dialogTranslate = totalEvents++;
public static final int didGenerateFingerprintKeyPair = totalEvents++;
public static final int walletPendingTransactionsChanged = totalEvents++;
public static final int walletSyncProgressChanged = totalEvents++;
public static final int httpFileDidLoad = totalEvents++;
public static final int httpFileDidFailedLoad = totalEvents++;
public static final int didUpdateConnectionState = totalEvents++;
public static final int fileUploaded = totalEvents++;
@ -267,6 +261,8 @@ public class NotificationCenter {
public static final int starGiftsLoaded = totalEvents++;
public static final int starUserGiftsLoaded = totalEvents++;
public static final int starGiftSoldOut = totalEvents++;
public static final int updateStories = totalEvents++;
public static final int botDownloadsUpdate = totalEvents++;
//global
public static final int pushMessagesUpdated = totalEvents++;

View file

@ -2179,6 +2179,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()) {
@ -4071,7 +4073,7 @@ public class NotificationsController extends BaseController {
return;
}
if (replace) {
if (chat != null) {
if (chat != null && allowSummary) {
message = message.replace(" @ " + name, "");
} else {
if (text[0]) {

View file

@ -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();

View file

@ -2321,7 +2321,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--;
@ -2330,9 +2331,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;
@ -2341,6 +2344,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;
@ -2380,17 +2384,19 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
newMsgObj1.id = message.id;
sentCount++;
if (scheduleDate != 0 && !currentSchedule) {
if (scheduled != currentSchedule) {
final int fromMode = scheduled ? ChatActivity.MODE_SCHEDULED : 0;
final int toMode = currentSchedule ? ChatActivity.MODE_SCHEDULED : 0;
AndroidUtilities.runOnUIThread(() -> {
getMessagesStorage().getStorageQueue().postRunnable(() -> {
getMessagesStorage().putMessages(sentMessages, true, false, false, 0, toMode, 0);
AndroidUtilities.runOnUIThread(() -> {
ArrayList<Integer> 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);
AndroidUtilities.runOnUIThread(() -> {
getMessagesController().deleteMessages(messageIds, null, null, newMsgObj1.dialog_id, false, fromMode, false, 0, null, 0, toMode == ChatActivity.MODE_SCHEDULED, message.id);
ArrayList<MessageObject> messageObjects = new ArrayList<>();
messageObjects.add(new MessageObject(msgObj.currentAccount, msgObj.messageOwner, true, true));
getMessagesController().updateInterfaceWithMessages(newMsgObj1.dialog_id, messageObjects, 0);
getMessagesController().updateInterfaceWithMessages(newMsgObj1.dialog_id, messageObjects, toMode);
getMediaDataController().increasePeerRaiting(newMsgObj1.dialog_id);
processSentMessage(oldId);
removeFromSendingMessages(oldId, scheduleDate != 0);
@ -2851,6 +2857,9 @@ public 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;
@ -2861,6 +2870,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;
@ -6094,6 +6105,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
final TLRPC.Updates updates = (TLRPC.Updates) response;
ArrayList<TLRPC.Update> updatesArr = ((TLRPC.Updates) response).updates;
LongSparseArray<SparseArray<TLRPC.MessageReplies>> channelReplies = null;
boolean currentSchedule = scheduled;
for (int a = 0; a < updatesArr.size(); a++) {
TLRPC.Update update = updatesArr.get(a);
if (update instanceof TLRPC.TL_updateMessageID) {
@ -6102,6 +6114,7 @@ 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));
@ -6145,11 +6158,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);
@ -6162,6 +6177,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<Integer> oldIds = new ArrayList<>();
for (int i = 0; i < msgObjs.size(); i++) {
final MessageObject msgObj = msgObjs.get(i);
final String originalPath = originalPaths.get(i);
@ -6212,22 +6232,31 @@ 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);
});
@ -6432,7 +6461,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);
@ -6462,6 +6491,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(() -> {
@ -6470,20 +6500,41 @@ 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);
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) {
getMessagesStorage().putChannelViews(null, null, channelReplies, true);
getNotificationCenter().postNotificationName(NotificationCenter.didUpdateMessagesViews, null, null, channelReplies, true);
@ -6538,16 +6589,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<Integer> messageIds = new ArrayList<>();
messageIds.add(oldId);
getMessagesController().deleteMessages(messageIds, null, null, newMsgObj.dialog_id, 0, false, ChatActivity.MODE_SCHEDULED);
getMessagesStorage().getStorageQueue().postRunnable(() -> {
getMessagesStorage().putMessages(sentMessages, true, false, false, 0, false, 0, 0);
AndroidUtilities.runOnUIThread(() -> {
ArrayList<MessageObject> messageObjects = new ArrayList<>();
messageObjects.add(new MessageObject(msgObj.currentAccount, msgObj.messageOwner, true, true));
getMessagesController().updateInterfaceWithMessages(newMsgObj.dialog_id, messageObjects, 0);
getMessagesStorage().getStorageQueue().postRunnable(() -> {
getMessagesStorage().putMessages(sentMessages, true, false, false, 0, false, !scheduled ? ChatActivity.MODE_SCHEDULED : ChatActivity.MODE_DEFAULT, 0);
AndroidUtilities.runOnUIThread(() -> {
final int scheduledMessageId = finalCurrentSchedule && newMsgObj != null ? newMsgObj.id : 0;
getMessagesController().deleteMessages(messageIds, null, null, newMsgObj.dialog_id, false, scheduled ? ChatActivity.MODE_SCHEDULED : ChatActivity.MODE_DEFAULT, false, 0, null, 0, !scheduled && finalCurrentSchedule, scheduledMessageId);
getMessagesController().updateInterfaceWithMessages(newMsgObj.dialog_id, messageObjects, finalCurrentSchedule ? ChatActivity.MODE_SCHEDULED : ChatActivity.MODE_DEFAULT);
getMediaDataController().increasePeerRaiting(newMsgObj.dialog_id);
processSentMessage(oldId);
removeFromSendingMessages(oldId, scheduled);
@ -8775,6 +8828,29 @@ public 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;
@ -9475,6 +9551,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";

View file

@ -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<Boolean> callback = MediaDataController.getInstance(currentAccount).shortcutCallbacks.remove(req_id);
if (callback != null) {
AndroidUtilities.runOnUIThread(() -> {
callback.run(true);
});
}
}
}

View file

@ -27,13 +27,13 @@ public class UserNameResolver {
LruCache<String, CachedPeer> resolvedCache = new LruCache<>(100);
HashMap<String, ArrayList<Consumer<Long>>> resolvingConsumers = new HashMap<>();
public void resolve(String username, Consumer<Long> resolveConsumer) {
public int resolve(String username, Consumer<Long> 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<Consumer<Long>> 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<Consumer<Long>> finalConsumers = resolvingConsumers.remove(username);
if (finalConsumers == null) {
return;

View file

@ -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;
}

View file

@ -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);

View file

@ -255,14 +255,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;
}
@ -397,7 +397,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)) {
@ -420,15 +420,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) {
@ -445,6 +445,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;
}

View file

@ -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<CacheSpan> addListener(String key, Listener listener) {
return null;
}
@Override
public void removeListener(String key, Listener listener) {
}
@Override
public NavigableSet<CacheSpan> getCachedSpans(String key) {
return null;
}
@Override
public Set<String> 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;
}
};
}

View file

@ -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();
}
}

View file

@ -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<Frame> freeFrames = new ArrayList<>();
private final TreeSet<Frame> frames = new TreeSet<Frame>((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<Frame> 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<Frame> 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<Frame> i = frames.iterator();
final ArrayList<Long> 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();
}
}
}

View file

@ -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 {
final long now = System.currentTimeMillis();
long dt = now - rewindLastTime;
rewindLastTime = now;
final float speed = Math.max(0, -getRewindSpeed() * playSpeed);
dt *= speed;
rewindBackSeekPlayerPosition -= dt;
}
if (rewindBackSeekPlayerPosition < 0) {
rewindBackSeekPlayerPosition = 0;
} else if (rewindBackSeekPlayerPosition > duration) {
rewindBackSeekPlayerPosition = duration;
}
if (rewindByBackSeek && rewindLastTime - rewindLastUpdatePlayerTime > 350) {
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;
rewinding = false;
fastSeeking = false;
boolean awaitSeek = false;
if (videoPlayer != null || webView != null) {
if (rewindByBackSeek) {
seekTo(rewindBackSeekPlayerPosition);
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);
}
} 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,60 +240,11 @@ 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 (seekSpeedDrawable != null) {
seekSpeedDrawable.setShown(false, 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) {
@ -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);
}
}

View file

@ -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);
}
}
}

View file

@ -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);
// }

View file

@ -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);

View file

@ -81,8 +81,7 @@ public class TLRPC {
public static final int MESSAGE_FLAG_HAS_BOT_ID = 0x00000800;
public static final int MESSAGE_FLAG_EDITED = 0x00008000;
public static final int LAYER = 191;
public static final int LAYER = 193;
public static abstract class EmailVerifyPurpose extends TLObject {
@ -5835,6 +5834,9 @@ public 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));
@ -5945,6 +5947,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;
@ -8238,6 +8249,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));
@ -8423,6 +8440,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;
@ -12459,6 +12494,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));
@ -12551,6 +12589,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;
@ -28263,6 +28310,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));
@ -29989,7 +30039,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;
@ -30006,13 +30056,17 @@ public class TLRPC {
public long max_tip_amount;
public ArrayList<Long> suggested_tip_amounts = new ArrayList<>();
public String terms_url;
public int subscription_period;
public static TL_invoice TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) {
TL_invoice result = null;
switch (constructor) {
case 0x5db95a15:
case 0x49ee584:
result = new TL_invoice();
break;
case 0x5db95a15:
result = new TL_invoice_layer193();
break;
case 0x3e85a91b:
result = new TL_invoice_layer163();
break;
@ -30026,6 +30080,99 @@ public 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;
@ -33930,36 +34077,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));
@ -34151,6 +34304,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;
@ -36515,6 +36684,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);
@ -38401,12 +38573,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<Integer> messages = new ArrayList<>();
public ArrayList<Integer> sent_messages = new ArrayList<>();
public void readParams(AbstractSerializedData stream, boolean exception) {
flags = stream.readInt32(exception);
peer = Peer.TLdeserialize(stream, stream.readInt32(exception), exception);
int magic = stream.readInt32(exception);
if (magic != 0x1cb5c415) {
@ -38419,10 +38594,24 @@ public 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();
@ -38430,6 +38619,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));
}
}
}
}
@ -43655,10 +43852,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;
@ -43668,7 +43865,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);
}
@ -54167,6 +54364,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;
@ -54177,6 +54375,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;
@ -54296,6 +54495,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);
@ -54403,6 +54604,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) {
@ -66762,10 +66965,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) {
@ -66774,15 +66977,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);
@ -66790,7 +66993,7 @@ public class TLRPC {
public void serializeToStream(AbstractSerializedData stream) {
stream.writeInt32(constructor);
channel.serializeToStream(stream);
peer.serializeToStream(stream);
}
}
@ -69174,6 +69377,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
@ -70050,6 +70254,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);
@ -70171,6 +70376,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) {
@ -75101,6 +75307,7 @@ public class TLRPC {
public int flags;
public boolean fullsize;
public boolean fullscreen;
public long query_id;
public String url;
@ -75120,6 +75327,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);
}
@ -75129,6 +75337,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);
@ -75144,6 +75353,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;
@ -75162,6 +75372,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);
@ -75189,6 +75400,7 @@ public class TLRPC {
public int flags;
public boolean compact;
public boolean fullscreen;
public InputPeer peer;
public InputUser bot;
public String start_param;
@ -75202,6 +75414,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);
@ -75269,6 +75482,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;
@ -75283,6 +75497,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);
@ -75303,6 +75518,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;
@ -75318,6 +75534,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) {
@ -77414,13 +77631,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) {
@ -77432,7 +77649,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);
}
}
@ -78099,6 +78316,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;
@ -79954,19 +80195,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);
@ -79994,6 +80225,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;
@ -83769,4 +84044,103 @@ public class TLRPC {
}
}
public static class TL_messages_preparedInlineMessage extends TLObject {
public static final int constructor = 0xff57708d;
public long query_id;
public BotInlineResult result;
public ArrayList<InlineQueryPeerType> peer_types = new ArrayList<>();
public int cache_time;
public ArrayList<User> 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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -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

View file

@ -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);
}

Some files were not shown because too many files have changed in this diff Show more