mirror of
https://github.com/MGislv/NekoX.git
synced 2024-06-28 09:34:05 +00:00
parent
f563fbbb05
commit
55463a93db
|
@ -5,11 +5,11 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.android.support:support-v4:23.3.0'
|
||||
compile 'com.android.support:support-v4:23.4.0'
|
||||
compile "com.google.android.gms:play-services-gcm:8.4.0"
|
||||
compile "com.google.android.gms:play-services-maps:8.4.0"
|
||||
compile 'net.hockeyapp.android:HockeySDK:4.0.+'
|
||||
compile 'com.googlecode.mp4parser:isoparser:1.0.+'
|
||||
compile 'net.hockeyapp.android:HockeySDK:4.0.1'
|
||||
compile 'com.googlecode.mp4parser:isoparser:1.0.6'
|
||||
}
|
||||
|
||||
android {
|
||||
|
@ -63,7 +63,7 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
defaultConfig.versionCode = 803
|
||||
defaultConfig.versionCode = 821
|
||||
|
||||
sourceSets.main {
|
||||
jniLibs.srcDir 'libs'
|
||||
|
@ -112,9 +112,9 @@ android {
|
|||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 9
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 23
|
||||
versionName "3.9.0"
|
||||
versionName "3.10.1"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.telegram.messenger"
|
||||
android:installLocation="auto">
|
||||
|
||||
|
@ -62,6 +63,22 @@
|
|||
|
||||
<uses-library android:name="com.google.android.maps" android:required="false"/>
|
||||
|
||||
|
||||
<receiver
|
||||
tools:replace="android:enabled"
|
||||
android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
|
||||
android:enabled="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.gms.measurement.UPLOAD" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
tools:replace="android:enabled"
|
||||
android:name="com.google.android.gms.measurement.AppMeasurementService"
|
||||
android:enabled="false"
|
||||
android:exported="false" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.telegram.messenger"
|
||||
android:installLocation="auto">
|
||||
|
||||
|
@ -62,6 +63,21 @@
|
|||
|
||||
<uses-library android:name="com.google.android.maps" android:required="false"/>
|
||||
|
||||
<receiver
|
||||
tools:replace="android:enabled"
|
||||
android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
|
||||
android:enabled="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.gms.measurement.UPLOAD" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
tools:replace="android:enabled"
|
||||
android:name="com.google.android.gms.measurement.AppMeasurementService"
|
||||
android:enabled="false"
|
||||
android:exported="false" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
26
TMessagesProj/google-services.json
Normal file → Executable file
26
TMessagesProj/google-services.json
Normal file → Executable file
|
@ -18,22 +18,9 @@
|
|||
"oauth_client": [],
|
||||
"api_key": [],
|
||||
"services": {
|
||||
"analytics_service": {
|
||||
"status": 1
|
||||
},
|
||||
"cloud_messaging_service": {
|
||||
"status": 2,
|
||||
"apns_config": []
|
||||
},
|
||||
"appinvite_service": {
|
||||
"status": 1,
|
||||
"other_platform_oauth_client": []
|
||||
},
|
||||
"google_signin_service": {
|
||||
"status": 1
|
||||
},
|
||||
"ads_service": {
|
||||
"status": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -50,22 +37,9 @@
|
|||
"oauth_client": [],
|
||||
"api_key": [],
|
||||
"services": {
|
||||
"analytics_service": {
|
||||
"status": 1
|
||||
},
|
||||
"cloud_messaging_service": {
|
||||
"status": 2,
|
||||
"apns_config": []
|
||||
},
|
||||
"appinvite_service": {
|
||||
"status": 1,
|
||||
"other_platform_oauth_client": []
|
||||
},
|
||||
"google_signin_service": {
|
||||
"status": 1
|
||||
},
|
||||
"ads_service": {
|
||||
"status": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -265,6 +265,8 @@ else
|
|||
|
||||
else
|
||||
ifeq ($(TARGET_ARCH_ABI),x86)
|
||||
LOCAL_CPPFLAGS += -Dx86fix
|
||||
LOCAL_CFLAGS += -Dx86fix
|
||||
LOCAL_ARM_MODE := arm
|
||||
LOCAL_SRC_FILE += \
|
||||
./libyuv/source/row_x86.asm
|
||||
|
|
|
@ -12,10 +12,8 @@
|
|||
#define INCLUDE_LIBYUV_CONVERT_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
// TODO(fbarchard): Remove the following headers includes.
|
||||
#include "libyuv/convert_from.h"
|
||||
#include "libyuv/planar_functions.h"
|
||||
#include "libyuv/rotate.h"
|
||||
|
||||
#include "libyuv/rotate.h" // For enum RotationMode.
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libyuv {
|
||||
|
|
|
@ -12,10 +12,8 @@
|
|||
#define INCLUDE_LIBYUV_CONVERT_ARGB_H_
|
||||
|
||||
#include "libyuv/basic_types.h"
|
||||
// TODO(fbarchard): Remove the following headers includes
|
||||
#include "libyuv/convert_from.h"
|
||||
#include "libyuv/planar_functions.h"
|
||||
#include "libyuv/rotate.h"
|
||||
|
||||
#include "libyuv/rotate.h" // For enum RotationMode.
|
||||
|
||||
// TODO(fbarchard): This set of functions should exactly match convert.h
|
||||
// TODO(fbarchard): Add tests. Create random content of right size and convert
|
||||
|
|
|
@ -56,8 +56,6 @@ int I400Copy(const uint8* src_y, int src_stride_y,
|
|||
uint8* dst_y, int dst_stride_y,
|
||||
int width, int height);
|
||||
|
||||
// TODO(fbarchard): I420ToM420
|
||||
|
||||
LIBYUV_API
|
||||
int I420ToNV12(const uint8* src_y, int src_stride_y,
|
||||
const uint8* src_u, int src_stride_u,
|
||||
|
|
|
@ -62,7 +62,7 @@ static __inline int TestCpuFlag(int test_flag) {
|
|||
// For testing, allow CPU flags to be disabled.
|
||||
// ie MaskCpuFlags(~kCpuHasSSSE3) to disable SSSE3.
|
||||
// MaskCpuFlags(-1) to enable all cpu specific optimizations.
|
||||
// MaskCpuFlags(0) to disable all cpu specific optimizations.
|
||||
// MaskCpuFlags(1) to disable all cpu specific optimizations.
|
||||
LIBYUV_API
|
||||
void MaskCpuFlags(int enable_flags);
|
||||
|
||||
|
|
|
@ -288,6 +288,12 @@ int ARGBCopyAlpha(const uint8* src_argb, int src_stride_argb,
|
|||
uint8* dst_argb, int dst_stride_argb,
|
||||
int width, int height);
|
||||
|
||||
// Extract the alpha channel from ARGB.
|
||||
LIBYUV_API
|
||||
int ARGBExtractAlpha(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_a, int dst_stride_a,
|
||||
int width, int height);
|
||||
|
||||
// Copy Y channel to Alpha of ARGB.
|
||||
LIBYUV_API
|
||||
int ARGBCopyYToAlpha(const uint8* src_y, int src_stride_y,
|
||||
|
|
|
@ -104,6 +104,7 @@ extern "C" {
|
|||
#define HAS_ARGBTOUVROW_SSSE3
|
||||
#define HAS_ARGBTOYJROW_SSSE3
|
||||
#define HAS_ARGBTOYROW_SSSE3
|
||||
#define HAS_ARGBEXTRACTALPHAROW_SSE2
|
||||
#define HAS_BGRATOUVROW_SSSE3
|
||||
#define HAS_BGRATOYROW_SSSE3
|
||||
#define HAS_COPYROW_ERMS
|
||||
|
@ -179,8 +180,7 @@ extern "C" {
|
|||
|
||||
// The following functions fail on gcc/clang 32 bit with fpic and framepointer.
|
||||
// caveat: clangcl uses row_win.cc which works.
|
||||
#if defined(NDEBUG) || !(defined(_DEBUG) && defined(__i386__)) || \
|
||||
!defined(__i386__) || defined(_MSC_VER)
|
||||
#if !defined(x86fix)
|
||||
// TODO(fbarchard): fix build error on x86 debug
|
||||
// https://code.google.com/p/libyuv/issues/detail?id=524
|
||||
#define HAS_I411TOARGBROW_SSSE3
|
||||
|
@ -208,7 +208,7 @@ extern "C" {
|
|||
#define HAS_COPYROW_AVX
|
||||
#define HAS_H422TOARGBROW_AVX2
|
||||
#define HAS_I400TOARGBROW_AVX2
|
||||
#if !(defined(_DEBUG) && defined(__i386__))
|
||||
#if !defined(x86fix)
|
||||
// TODO(fbarchard): fix build error on android_full_debug=1
|
||||
// https://code.google.com/p/libyuv/issues/detail?id=517
|
||||
#define HAS_I422ALPHATOARGBROW_AVX2
|
||||
|
@ -291,6 +291,7 @@ extern "C" {
|
|||
#define HAS_ARGBTOUVROW_NEON
|
||||
#define HAS_ARGBTOYJROW_NEON
|
||||
#define HAS_ARGBTOYROW_NEON
|
||||
#define HAS_ARGBEXTRACTALPHAROW_NEON
|
||||
#define HAS_BGRATOUVROW_NEON
|
||||
#define HAS_BGRATOYROW_NEON
|
||||
#define HAS_COPYROW_NEON
|
||||
|
@ -877,6 +878,14 @@ void ARGBCopyAlphaRow_Any_SSE2(const uint8* src_argb, uint8* dst_argb,
|
|||
void ARGBCopyAlphaRow_Any_AVX2(const uint8* src_argb, uint8* dst_argb,
|
||||
int width);
|
||||
|
||||
void ARGBExtractAlphaRow_C(const uint8* src_argb, uint8* dst_a, int width);
|
||||
void ARGBExtractAlphaRow_SSE2(const uint8* src_argb, uint8* dst_a, int width);
|
||||
void ARGBExtractAlphaRow_NEON(const uint8* src_argb, uint8* dst_a, int width);
|
||||
void ARGBExtractAlphaRow_Any_SSE2(const uint8* src_argb, uint8* dst_a,
|
||||
int width);
|
||||
void ARGBExtractAlphaRow_Any_NEON(const uint8* src_argb, uint8* dst_a,
|
||||
int width);
|
||||
|
||||
void ARGBCopyYToAlphaRow_C(const uint8* src_y, uint8* dst_argb, int width);
|
||||
void ARGBCopyYToAlphaRow_SSE2(const uint8* src_y, uint8* dst_argb, int width);
|
||||
void ARGBCopyYToAlphaRow_AVX2(const uint8* src_y, uint8* dst_argb, int width);
|
||||
|
|
|
@ -11,6 +11,6 @@
|
|||
#ifndef INCLUDE_LIBYUV_VERSION_H_ // NOLINT
|
||||
#define INCLUDE_LIBYUV_VERSION_H_
|
||||
|
||||
#define LIBYUV_VERSION 1586
|
||||
#define LIBYUV_VERSION 1597
|
||||
|
||||
#endif // INCLUDE_LIBYUV_VERSION_H_ NOLINT
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#ifdef HAVE_JPEG
|
||||
#include "libyuv/mjpeg_decoder.h"
|
||||
#endif
|
||||
#include "libyuv/planar_functions.h" // For CopyPlane and ARGBShuffle.
|
||||
#include "libyuv/rotate_argb.h"
|
||||
#include "libyuv/row.h"
|
||||
#include "libyuv/video_common.h"
|
||||
|
|
|
@ -1077,7 +1077,6 @@ int ConvertFromI420(const uint8* y, int y_stride,
|
|||
// Triplanar formats
|
||||
// TODO(fbarchard): halfstride instead of halfwidth
|
||||
case FOURCC_I420:
|
||||
case FOURCC_YU12:
|
||||
case FOURCC_YV12: {
|
||||
int halfwidth = (width + 1) / 2;
|
||||
int halfheight = (height + 1) / 2;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
*/
|
||||
|
||||
#include "libyuv/convert.h"
|
||||
#include "libyuv/convert_argb.h"
|
||||
|
||||
#ifdef HAVE_JPEG
|
||||
#include "libyuv/mjpeg_decoder.h"
|
||||
|
|
|
@ -176,7 +176,6 @@ int ConvertToARGB(const uint8* sample, size_t sample_size,
|
|||
break;
|
||||
// Triplanar formats
|
||||
case FOURCC_I420:
|
||||
case FOURCC_YU12:
|
||||
case FOURCC_YV12: {
|
||||
const uint8* src_y = sample + (src_width * crop_y + crop_x);
|
||||
const uint8* src_u;
|
||||
|
|
|
@ -39,12 +39,13 @@ int ConvertToI420(const uint8* sample,
|
|||
int aligned_src_width = (src_width + 1) & ~1;
|
||||
const uint8* src;
|
||||
const uint8* src_uv;
|
||||
int abs_src_height = (src_height < 0) ? -src_height : src_height;
|
||||
int inv_crop_height = (crop_height < 0) ? -crop_height : crop_height;
|
||||
const int abs_src_height = (src_height < 0) ? -src_height : src_height;
|
||||
// TODO(nisse): Why allow crop_height < 0?
|
||||
const int abs_crop_height = (crop_height < 0) ? -crop_height : crop_height;
|
||||
int r = 0;
|
||||
LIBYUV_BOOL need_buf = (rotation && format != FOURCC_I420 &&
|
||||
format != FOURCC_NV12 && format != FOURCC_NV21 &&
|
||||
format != FOURCC_YU12 && format != FOURCC_YV12) || y == sample;
|
||||
format != FOURCC_YV12) || y == sample;
|
||||
uint8* tmp_y = y;
|
||||
uint8* tmp_u = u;
|
||||
uint8* tmp_v = v;
|
||||
|
@ -52,16 +53,14 @@ int ConvertToI420(const uint8* sample,
|
|||
int tmp_u_stride = u_stride;
|
||||
int tmp_v_stride = v_stride;
|
||||
uint8* rotate_buffer = NULL;
|
||||
int abs_crop_height = (crop_height < 0) ? -crop_height : crop_height;
|
||||
const int inv_crop_height =
|
||||
(src_height < 0) ? -abs_crop_height : abs_crop_height;
|
||||
|
||||
if (!y || !u || !v || !sample ||
|
||||
src_width <= 0 || crop_width <= 0 ||
|
||||
src_height == 0 || crop_height == 0) {
|
||||
return -1;
|
||||
}
|
||||
if (src_height < 0) {
|
||||
inv_crop_height = -inv_crop_height;
|
||||
}
|
||||
|
||||
// One pass rotation is available for some formats. For the rest, convert
|
||||
// to I420 (with optional vertical flipping) into a temporary I420 buffer,
|
||||
|
@ -214,7 +213,6 @@ int ConvertToI420(const uint8* sample,
|
|||
break;
|
||||
// Triplanar formats
|
||||
case FOURCC_I420:
|
||||
case FOURCC_YU12:
|
||||
case FOURCC_YV12: {
|
||||
const uint8* src_y = sample + (src_width * crop_y + crop_x);
|
||||
const uint8* src_u;
|
||||
|
|
|
@ -2374,6 +2374,49 @@ int ARGBCopyAlpha(const uint8* src_argb, int src_stride_argb,
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Extract just the alpha channel from ARGB.
|
||||
LIBYUV_API
|
||||
int ARGBExtractAlpha(const uint8* src_argb, int src_stride,
|
||||
uint8* dst_a, int dst_stride,
|
||||
int width, int height) {
|
||||
if (!src_argb || !dst_a || width <= 0 || height == 0) {
|
||||
return -1;
|
||||
}
|
||||
// Negative height means invert the image.
|
||||
if (height < 0) {
|
||||
height = -height;
|
||||
src_argb += (height - 1) * src_stride;
|
||||
src_stride = -src_stride;
|
||||
}
|
||||
// Coalesce rows.
|
||||
if (src_stride == width * 4 && dst_stride == width) {
|
||||
width *= height;
|
||||
height = 1;
|
||||
src_stride = dst_stride = 0;
|
||||
}
|
||||
void (*ARGBExtractAlphaRow)(const uint8 *src_argb, uint8 *dst_a, int width) =
|
||||
ARGBExtractAlphaRow_C;
|
||||
#if defined(HAS_ARGBEXTRACTALPHAROW_SSE2)
|
||||
if (TestCpuFlag(kCpuHasSSE2)) {
|
||||
ARGBExtractAlphaRow = IS_ALIGNED(width, 8) ? ARGBExtractAlphaRow_SSE2
|
||||
: ARGBExtractAlphaRow_Any_SSE2;
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_ARGBEXTRACTALPHAROW_NEON)
|
||||
if (TestCpuFlag(kCpuHasNEON)) {
|
||||
ARGBExtractAlphaRow = IS_ALIGNED(width, 16) ? ARGBExtractAlphaRow_NEON
|
||||
: ARGBExtractAlphaRow_Any_NEON;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int y = 0; y < height; ++y) {
|
||||
ARGBExtractAlphaRow(src_argb, dst_a, width);
|
||||
src_argb += src_stride;
|
||||
dst_a += dst_stride;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy a planar Y channel to the alpha channel of a destination ARGB image.
|
||||
LIBYUV_API
|
||||
int ARGBCopyYToAlpha(const uint8* src_y, int src_stride_y,
|
||||
|
|
|
@ -23,7 +23,7 @@ extern "C" {
|
|||
(_MIPS_SIM == _MIPS_SIM_ABI32)
|
||||
|
||||
void TransposeWx8_DSPR2(const uint8* src, int src_stride,
|
||||
uint8* dst, int dst_stride, int width) {
|
||||
uint8* dst, int dst_stride, int width) {
|
||||
__asm__ __volatile__ (
|
||||
".set push \n"
|
||||
".set noreorder \n"
|
||||
|
@ -107,7 +107,7 @@ void TransposeWx8_DSPR2(const uint8* src, int src_stride,
|
|||
}
|
||||
|
||||
void TransposeWx8_Fast_DSPR2(const uint8* src, int src_stride,
|
||||
uint8* dst, int dst_stride, int width) {
|
||||
uint8* dst, int dst_stride, int width) {
|
||||
__asm__ __volatile__ (
|
||||
".set noat \n"
|
||||
".set push \n"
|
||||
|
@ -309,9 +309,9 @@ void TransposeWx8_Fast_DSPR2(const uint8* src, int src_stride,
|
|||
}
|
||||
|
||||
void TransposeUVWx8_DSPR2(const uint8* src, int src_stride,
|
||||
uint8* dst_a, int dst_stride_a,
|
||||
uint8* dst_b, int dst_stride_b,
|
||||
int width) {
|
||||
uint8* dst_a, int dst_stride_a,
|
||||
uint8* dst_b, int dst_stride_b,
|
||||
int width) {
|
||||
__asm__ __volatile__ (
|
||||
".set push \n"
|
||||
".set noreorder \n"
|
||||
|
|
|
@ -466,38 +466,15 @@ ANY11(ARGBUnattenuateRow_Any_AVX2, ARGBUnattenuateRow_AVX2, 0, 4, 4, 7)
|
|||
#ifdef HAS_ARGBATTENUATEROW_NEON
|
||||
ANY11(ARGBAttenuateRow_Any_NEON, ARGBAttenuateRow_NEON, 0, 4, 4, 7)
|
||||
#endif
|
||||
#ifdef HAS_ARGBEXTRACTALPHAROW_SSE2
|
||||
ANY11(ARGBExtractAlphaRow_Any_SSE2, ARGBExtractAlphaRow_SSE2, 0, 4, 1, 7)
|
||||
#endif
|
||||
#ifdef HAS_ARGBEXTRACTALPHAROW_NEON
|
||||
ANY11(ARGBExtractAlphaRow_Any_NEON, ARGBExtractAlphaRow_NEON, 0, 4, 1, 15)
|
||||
#endif
|
||||
#undef ANY11
|
||||
|
||||
// Any 1 to 1 with yuvconstants
|
||||
#define ANY11C(NAMEANY, ANY_SIMD, UVSHIFT, SBPP, BPP, MASK) \
|
||||
void NAMEANY(const uint8* src_ptr, uint8* dst_ptr, \
|
||||
const struct YuvConstants* yuvconstants, int width) { \
|
||||
SIMD_ALIGNED(uint8 temp[128 * 2]); \
|
||||
memset(temp, 0, 128); /* for YUY2 and msan */ \
|
||||
int r = width & MASK; \
|
||||
int n = width & ~MASK; \
|
||||
if (n > 0) { \
|
||||
ANY_SIMD(src_ptr, dst_ptr, yuvconstants, n); \
|
||||
} \
|
||||
memcpy(temp, src_ptr + (n >> UVSHIFT) * SBPP, SS(r, UVSHIFT) * SBPP); \
|
||||
ANY_SIMD(temp, temp + 128, yuvconstants, MASK + 1); \
|
||||
memcpy(dst_ptr + n * BPP, temp + 128, r * BPP); \
|
||||
}
|
||||
#if defined(HAS_YUY2TOARGBROW_SSSE3)
|
||||
ANY11C(YUY2ToARGBRow_Any_SSSE3, YUY2ToARGBRow_SSSE3, 1, 4, 4, 15)
|
||||
ANY11C(UYVYToARGBRow_Any_SSSE3, UYVYToARGBRow_SSSE3, 1, 4, 4, 15)
|
||||
#endif
|
||||
#if defined(HAS_YUY2TOARGBROW_AVX2)
|
||||
ANY11C(YUY2ToARGBRow_Any_AVX2, YUY2ToARGBRow_AVX2, 1, 4, 4, 31)
|
||||
ANY11C(UYVYToARGBRow_Any_AVX2, UYVYToARGBRow_AVX2, 1, 4, 4, 31)
|
||||
#endif
|
||||
#if defined(HAS_YUY2TOARGBROW_NEON)
|
||||
ANY11C(YUY2ToARGBRow_Any_NEON, YUY2ToARGBRow_NEON, 1, 4, 4, 7)
|
||||
ANY11C(UYVYToARGBRow_Any_NEON, UYVYToARGBRow_NEON, 1, 4, 4, 7)
|
||||
#endif
|
||||
#undef ANY11C
|
||||
|
||||
// Any 1 to 1 blended.
|
||||
// Any 1 to 1 blended. Destination is read, modify, write.
|
||||
#define ANY11B(NAMEANY, ANY_SIMD, UVSHIFT, SBPP, BPP, MASK) \
|
||||
void NAMEANY(const uint8* src_ptr, uint8* dst_ptr, int width) { \
|
||||
SIMD_ALIGNED(uint8 temp[128 * 2]); \
|
||||
|
@ -516,7 +493,7 @@ ANY11C(UYVYToARGBRow_Any_NEON, UYVYToARGBRow_NEON, 1, 4, 4, 7)
|
|||
#ifdef HAS_ARGBCOPYALPHAROW_AVX2
|
||||
ANY11B(ARGBCopyAlphaRow_Any_AVX2, ARGBCopyAlphaRow_AVX2, 0, 4, 4, 15)
|
||||
#endif
|
||||
#ifdef HAS_ARGBCOPYYTOALPHAROW_SSE2
|
||||
#ifdef HAS_ARGBCOPYALPHAROW_SSE2
|
||||
ANY11B(ARGBCopyAlphaRow_Any_SSE2, ARGBCopyAlphaRow_SSE2, 0, 4, 4, 7)
|
||||
#endif
|
||||
#ifdef HAS_ARGBCOPYYTOALPHAROW_AVX2
|
||||
|
@ -569,6 +546,35 @@ ANY11P(ARGBShuffleRow_Any_NEON, ARGBShuffleRow_NEON, const uint8*, 4, 4, 3)
|
|||
#endif
|
||||
#undef ANY11P
|
||||
|
||||
// Any 1 to 1 with yuvconstants
|
||||
#define ANY11C(NAMEANY, ANY_SIMD, UVSHIFT, SBPP, BPP, MASK) \
|
||||
void NAMEANY(const uint8* src_ptr, uint8* dst_ptr, \
|
||||
const struct YuvConstants* yuvconstants, int width) { \
|
||||
SIMD_ALIGNED(uint8 temp[128 * 2]); \
|
||||
memset(temp, 0, 128); /* for YUY2 and msan */ \
|
||||
int r = width & MASK; \
|
||||
int n = width & ~MASK; \
|
||||
if (n > 0) { \
|
||||
ANY_SIMD(src_ptr, dst_ptr, yuvconstants, n); \
|
||||
} \
|
||||
memcpy(temp, src_ptr + (n >> UVSHIFT) * SBPP, SS(r, UVSHIFT) * SBPP); \
|
||||
ANY_SIMD(temp, temp + 128, yuvconstants, MASK + 1); \
|
||||
memcpy(dst_ptr + n * BPP, temp + 128, r * BPP); \
|
||||
}
|
||||
#if defined(HAS_YUY2TOARGBROW_SSSE3)
|
||||
ANY11C(YUY2ToARGBRow_Any_SSSE3, YUY2ToARGBRow_SSSE3, 1, 4, 4, 15)
|
||||
ANY11C(UYVYToARGBRow_Any_SSSE3, UYVYToARGBRow_SSSE3, 1, 4, 4, 15)
|
||||
#endif
|
||||
#if defined(HAS_YUY2TOARGBROW_AVX2)
|
||||
ANY11C(YUY2ToARGBRow_Any_AVX2, YUY2ToARGBRow_AVX2, 1, 4, 4, 31)
|
||||
ANY11C(UYVYToARGBRow_Any_AVX2, UYVYToARGBRow_AVX2, 1, 4, 4, 31)
|
||||
#endif
|
||||
#if defined(HAS_YUY2TOARGBROW_NEON)
|
||||
ANY11C(YUY2ToARGBRow_Any_NEON, YUY2ToARGBRow_NEON, 1, 4, 4, 7)
|
||||
ANY11C(UYVYToARGBRow_Any_NEON, UYVYToARGBRow_NEON, 1, 4, 4, 7)
|
||||
#endif
|
||||
#undef ANY11C
|
||||
|
||||
// Any 1 to 1 interpolate. Takes 2 rows of source via stride.
|
||||
#define ANY11T(NAMEANY, ANY_SIMD, SBPP, BPP, MASK) \
|
||||
void NAMEANY(uint8* dst_ptr, const uint8* src_ptr, \
|
||||
|
|
|
@ -2381,6 +2381,19 @@ void ARGBCopyAlphaRow_C(const uint8* src, uint8* dst, int width) {
|
|||
}
|
||||
}
|
||||
|
||||
void ARGBExtractAlphaRow_C(const uint8* src_argb, uint8* dst_a, int width) {
|
||||
int i;
|
||||
for (i = 0; i < width - 1; i += 2) {
|
||||
dst_a[0] = src_argb[3];
|
||||
dst_a[1] = src_argb[7];
|
||||
dst_a += 2;
|
||||
src_argb += 8;
|
||||
}
|
||||
if (width & 1) {
|
||||
dst_a[0] = src_argb[3];
|
||||
}
|
||||
}
|
||||
|
||||
void ARGBCopyYToAlphaRow_C(const uint8* src, uint8* dst, int width) {
|
||||
int i;
|
||||
for (i = 0; i < width - 1; i += 2) {
|
||||
|
|
|
@ -2936,6 +2936,33 @@ void ARGBCopyAlphaRow_AVX2(const uint8* src, uint8* dst, int width) {
|
|||
}
|
||||
#endif // HAS_ARGBCOPYALPHAROW_AVX2
|
||||
|
||||
#ifdef HAS_ARGBEXTRACTALPHAROW_SSE2
|
||||
// width in pixels
|
||||
void ARGBExtractAlphaRow_SSE2(const uint8* src_argb, uint8* dst_a, int width) {
|
||||
asm volatile (
|
||||
LABELALIGN
|
||||
"1: \n"
|
||||
"movdqu " MEMACCESS(0) ", %%xmm0 \n"
|
||||
"movdqu " MEMACCESS2(0x10, 0) ", %%xmm1 \n"
|
||||
"lea " MEMLEA(0x20, 0) ", %0 \n"
|
||||
"psrld $0x18, %%xmm0 \n"
|
||||
"psrld $0x18, %%xmm1 \n"
|
||||
"packssdw %%xmm1, %%xmm0 \n"
|
||||
"packuswb %%xmm0, %%xmm0 \n"
|
||||
"movq %%xmm0," MEMACCESS(1) " \n"
|
||||
"lea " MEMLEA(0x8, 1) ", %1 \n"
|
||||
"sub $0x8, %2 \n"
|
||||
"jg 1b \n"
|
||||
: "+r"(src_argb), // %0
|
||||
"+r"(dst_a), // %1
|
||||
"+rm"(width) // %2
|
||||
:
|
||||
: "memory", "cc"
|
||||
, "xmm0", "xmm1"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBEXTRACTALPHAROW_SSE2
|
||||
|
||||
#ifdef HAS_ARGBCOPYYTOALPHAROW_SSE2
|
||||
// width in pixels
|
||||
void ARGBCopyYToAlphaRow_SSE2(const uint8* src, uint8* dst, int width) {
|
||||
|
@ -3569,7 +3596,7 @@ void BlendPlaneRow_SSSE3(const uint8* src0, const uint8* src1,
|
|||
"+r"(src1), // %1
|
||||
"+r"(alpha), // %2
|
||||
"+r"(dst), // %3
|
||||
"+r"(width) // %4
|
||||
"+rm"(width) // %4
|
||||
:: "memory", "cc", "eax", "xmm0", "xmm1", "xmm2", "xmm5", "xmm6", "xmm7"
|
||||
);
|
||||
}
|
||||
|
@ -3626,7 +3653,7 @@ void BlendPlaneRow_AVX2(const uint8* src0, const uint8* src1,
|
|||
"+r"(src1), // %1
|
||||
"+r"(alpha), // %2
|
||||
"+r"(dst), // %3
|
||||
"+r"(width) // %4
|
||||
"+rm"(width) // %4
|
||||
:: "memory", "cc", "eax",
|
||||
"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"
|
||||
);
|
||||
|
@ -4909,9 +4936,9 @@ void InterpolateRow_SSSE3(uint8* dst_ptr, const uint8* src_ptr,
|
|||
"jg 100b \n"
|
||||
|
||||
"99: \n"
|
||||
: "+r"(dst_ptr), // %0
|
||||
"+r"(src_ptr), // %1
|
||||
"+r"(dst_width), // %2
|
||||
: "+r"(dst_ptr), // %0
|
||||
"+r"(src_ptr), // %1
|
||||
"+rm"(dst_width), // %2
|
||||
"+r"(source_y_fraction) // %3
|
||||
: "r"((intptr_t)(src_stride)) // %4
|
||||
: "memory", "cc", "eax", NACL_R14
|
||||
|
@ -4987,7 +5014,7 @@ void InterpolateRow_AVX2(uint8* dst_ptr, const uint8* src_ptr,
|
|||
"999: \n"
|
||||
: "+D"(dst_ptr), // %0
|
||||
"+S"(src_ptr), // %1
|
||||
"+c"(dst_width), // %2
|
||||
"+cm"(dst_width), // %2
|
||||
"+r"(source_y_fraction) // %3
|
||||
: "r"((intptr_t)(src_stride)) // %4
|
||||
: "memory", "cc", "eax", NACL_R14
|
||||
|
|
|
@ -381,7 +381,7 @@ void CopyRow_MIPS(const uint8* src, uint8* dst, int count) {
|
|||
(_MIPS_SIM == _MIPS_SIM_ABI32) && (__mips_isa_rev < 6)
|
||||
|
||||
void SplitUVRow_DSPR2(const uint8* src_uv, uint8* dst_u, uint8* dst_v,
|
||||
int width) {
|
||||
int width) {
|
||||
__asm__ __volatile__ (
|
||||
".set push \n"
|
||||
".set noreorder \n"
|
||||
|
@ -497,7 +497,7 @@ void MirrorRow_DSPR2(const uint8* src, uint8* dst, int width) {
|
|||
}
|
||||
|
||||
void MirrorUVRow_DSPR2(const uint8* src_uv, uint8* dst_u, uint8* dst_v,
|
||||
int width) {
|
||||
int width) {
|
||||
int x;
|
||||
int y;
|
||||
__asm__ __volatile__ (
|
||||
|
@ -654,11 +654,11 @@ void MirrorUVRow_DSPR2(const uint8* src_uv, uint8* dst_u, uint8* dst_v,
|
|||
|
||||
// TODO(fbarchard): accept yuv conversion constants.
|
||||
void I422ToARGBRow_DSPR2(const uint8* y_buf,
|
||||
const uint8* u_buf,
|
||||
const uint8* v_buf,
|
||||
uint8* rgb_buf,
|
||||
const struct YuvConstants* yuvconstants,
|
||||
int width) {
|
||||
const uint8* u_buf,
|
||||
const uint8* v_buf,
|
||||
uint8* rgb_buf,
|
||||
const struct YuvConstants* yuvconstants,
|
||||
int width) {
|
||||
__asm__ __volatile__ (
|
||||
".set push \n"
|
||||
".set noreorder \n"
|
||||
|
@ -717,8 +717,8 @@ void I422ToARGBRow_DSPR2(const uint8* y_buf,
|
|||
|
||||
// Bilinear filter 8x2 -> 8x1
|
||||
void InterpolateRow_DSPR2(uint8* dst_ptr, const uint8* src_ptr,
|
||||
ptrdiff_t src_stride, int dst_width,
|
||||
int source_y_fraction) {
|
||||
ptrdiff_t src_stride, int dst_width,
|
||||
int source_y_fraction) {
|
||||
int y0_fraction = 256 - source_y_fraction;
|
||||
const uint8* src_ptr1 = src_ptr + src_stride;
|
||||
|
||||
|
|
|
@ -1298,6 +1298,24 @@ void ARGBToYRow_NEON(const uint8* src_argb, uint8* dst_y, int width) {
|
|||
);
|
||||
}
|
||||
|
||||
void ARGBExtractAlphaRow_NEON(const uint8* src_argb, uint8* dst_a, int width) {
|
||||
asm volatile (
|
||||
"1: \n"
|
||||
MEMACCESS(0)
|
||||
"vld4.8 {d0, d2, d4, d6}, [%0]! \n" // load 8 ARGB pixels
|
||||
"vld4.8 {d1, d3, d5, d7}, [%0]! \n" // load next 8 ARGB pixels
|
||||
"subs %2, %2, #16 \n" // 16 processed per loop
|
||||
MEMACCESS(1)
|
||||
"vst1.8 {q3}, [%1]! \n" // store 16 A's.
|
||||
"bgt 1b \n"
|
||||
: "+r"(src_argb), // %0
|
||||
"+r"(dst_a), // %1
|
||||
"+r"(width) // %2
|
||||
:
|
||||
: "cc", "memory", "q0", "q1", "q2", "q3" // Clobber List
|
||||
);
|
||||
}
|
||||
|
||||
void ARGBToYJRow_NEON(const uint8* src_argb, uint8* dst_y, int width) {
|
||||
asm volatile (
|
||||
"vmov.u8 d24, #15 \n" // B * 0.11400 coefficient
|
||||
|
@ -2565,8 +2583,6 @@ void ARGBColorMatrixRow_NEON(const uint8* src_argb, uint8* dst_argb,
|
|||
);
|
||||
}
|
||||
|
||||
// TODO(fbarchard): fix vqshrun in ARGBMultiplyRow_NEON and reenable.
|
||||
#ifdef HAS_ARGBMULTIPLYROW_NEON
|
||||
// Multiply 2 rows of ARGB pixels together, 8 pixels at a time.
|
||||
void ARGBMultiplyRow_NEON(const uint8* src_argb0, const uint8* src_argb1,
|
||||
uint8* dst_argb, int width) {
|
||||
|
@ -2598,7 +2614,6 @@ void ARGBMultiplyRow_NEON(const uint8* src_argb0, const uint8* src_argb1,
|
|||
: "cc", "memory", "q0", "q1", "q2", "q3"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBMULTIPLYROW_NEON
|
||||
|
||||
// Add 2 rows of ARGB pixels together, 8 pixels at a time.
|
||||
void ARGBAddRow_NEON(const uint8* src_argb0, const uint8* src_argb1,
|
||||
|
|
|
@ -127,7 +127,6 @@ extern "C" {
|
|||
"sqshrun " #vG ".8b, " #vG ".8h, #6 \n" /* G */ \
|
||||
"sqshrun " #vR ".8b, " #vR ".8h, #6 \n" /* R */ \
|
||||
|
||||
#ifdef HAS_I444TOARGBROW_NEON
|
||||
void I444ToARGBRow_NEON(const uint8* src_y,
|
||||
const uint8* src_u,
|
||||
const uint8* src_v,
|
||||
|
@ -157,9 +156,7 @@ void I444ToARGBRow_NEON(const uint8* src_y,
|
|||
"v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30"
|
||||
);
|
||||
}
|
||||
#endif // HAS_I444TOARGBROW_NEON
|
||||
|
||||
#ifdef HAS_I422TOARGBROW_NEON
|
||||
void I422ToARGBRow_NEON(const uint8* src_y,
|
||||
const uint8* src_u,
|
||||
const uint8* src_v,
|
||||
|
@ -189,9 +186,7 @@ void I422ToARGBRow_NEON(const uint8* src_y,
|
|||
"v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30"
|
||||
);
|
||||
}
|
||||
#endif // HAS_I422TOARGBROW_NEON
|
||||
|
||||
#ifdef HAS_I422ALPHATOARGBROW_NEON
|
||||
void I422AlphaToARGBRow_NEON(const uint8* src_y,
|
||||
const uint8* src_u,
|
||||
const uint8* src_v,
|
||||
|
@ -224,9 +219,7 @@ void I422AlphaToARGBRow_NEON(const uint8* src_y,
|
|||
"v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30"
|
||||
);
|
||||
}
|
||||
#endif // HAS_I422ALPHATOARGBROW_NEON
|
||||
|
||||
#ifdef HAS_I411TOARGBROW_NEON
|
||||
void I411ToARGBRow_NEON(const uint8* src_y,
|
||||
const uint8* src_u,
|
||||
const uint8* src_v,
|
||||
|
@ -256,9 +249,7 @@ void I411ToARGBRow_NEON(const uint8* src_y,
|
|||
"v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30"
|
||||
);
|
||||
}
|
||||
#endif // HAS_I411TOARGBROW_NEON
|
||||
|
||||
#ifdef HAS_I422TORGBAROW_NEON
|
||||
void I422ToRGBARow_NEON(const uint8* src_y,
|
||||
const uint8* src_u,
|
||||
const uint8* src_v,
|
||||
|
@ -288,9 +279,7 @@ void I422ToRGBARow_NEON(const uint8* src_y,
|
|||
"v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30"
|
||||
);
|
||||
}
|
||||
#endif // HAS_I422TORGBAROW_NEON
|
||||
|
||||
#ifdef HAS_I422TORGB24ROW_NEON
|
||||
void I422ToRGB24Row_NEON(const uint8* src_y,
|
||||
const uint8* src_u,
|
||||
const uint8* src_v,
|
||||
|
@ -319,7 +308,6 @@ void I422ToRGB24Row_NEON(const uint8* src_y,
|
|||
"v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30"
|
||||
);
|
||||
}
|
||||
#endif // HAS_I422TORGB24ROW_NEON
|
||||
|
||||
#define ARGBTORGB565 \
|
||||
"shll v0.8h, v22.8b, #8 \n" /* R */ \
|
||||
|
@ -328,7 +316,6 @@ void I422ToRGB24Row_NEON(const uint8* src_y,
|
|||
"sri v0.8h, v21.8h, #5 \n" /* RG */ \
|
||||
"sri v0.8h, v20.8h, #11 \n" /* RGB */
|
||||
|
||||
#ifdef HAS_I422TORGB565ROW_NEON
|
||||
void I422ToRGB565Row_NEON(const uint8* src_y,
|
||||
const uint8* src_u,
|
||||
const uint8* src_v,
|
||||
|
@ -358,7 +345,6 @@ void I422ToRGB565Row_NEON(const uint8* src_y,
|
|||
"v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30"
|
||||
);
|
||||
}
|
||||
#endif // HAS_I422TORGB565ROW_NEON
|
||||
|
||||
#define ARGBTOARGB1555 \
|
||||
"shll v0.8h, v23.8b, #8 \n" /* A */ \
|
||||
|
@ -369,7 +355,6 @@ void I422ToRGB565Row_NEON(const uint8* src_y,
|
|||
"sri v0.8h, v21.8h, #6 \n" /* ARG */ \
|
||||
"sri v0.8h, v20.8h, #11 \n" /* ARGB */
|
||||
|
||||
#ifdef HAS_I422TOARGB1555ROW_NEON
|
||||
void I422ToARGB1555Row_NEON(const uint8* src_y,
|
||||
const uint8* src_u,
|
||||
const uint8* src_v,
|
||||
|
@ -400,7 +385,6 @@ void I422ToARGB1555Row_NEON(const uint8* src_y,
|
|||
"v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30"
|
||||
);
|
||||
}
|
||||
#endif // HAS_I422TOARGB1555ROW_NEON
|
||||
|
||||
#define ARGBTOARGB4444 \
|
||||
/* Input v20.8b<=B, v21.8b<=G, v22.8b<=R, v23.8b<=A, v4.8b<=0x0f */ \
|
||||
|
@ -412,7 +396,6 @@ void I422ToARGB1555Row_NEON(const uint8* src_y,
|
|||
"orr v1.8b, v22.8b, v23.8b \n" /* RA */ \
|
||||
"zip1 v0.16b, v0.16b, v1.16b \n" /* BGRA */
|
||||
|
||||
#ifdef HAS_I422TOARGB4444ROW_NEON
|
||||
void I422ToARGB4444Row_NEON(const uint8* src_y,
|
||||
const uint8* src_u,
|
||||
const uint8* src_v,
|
||||
|
@ -444,13 +427,10 @@ void I422ToARGB4444Row_NEON(const uint8* src_y,
|
|||
"v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30"
|
||||
);
|
||||
}
|
||||
#endif // HAS_I422TOARGB4444ROW_NEON
|
||||
|
||||
#ifdef HAS_I400TOARGBROW_NEON
|
||||
void I400ToARGBRow_NEON(const uint8* src_y,
|
||||
uint8* dst_argb,
|
||||
int width) {
|
||||
int64 width64 = (int64)(width);
|
||||
asm volatile (
|
||||
YUVTORGB_SETUP
|
||||
"movi v23.8b, #255 \n"
|
||||
|
@ -463,7 +443,7 @@ void I400ToARGBRow_NEON(const uint8* src_y,
|
|||
"b.gt 1b \n"
|
||||
: "+r"(src_y), // %0
|
||||
"+r"(dst_argb), // %1
|
||||
"+r"(width64) // %2
|
||||
"+r"(width) // %2
|
||||
: [kUVToRB]"r"(&kYuvI601Constants.kUVToRB),
|
||||
[kUVToG]"r"(&kYuvI601Constants.kUVToG),
|
||||
[kUVBiasBGR]"r"(&kYuvI601Constants.kUVBiasBGR),
|
||||
|
@ -472,9 +452,7 @@ void I400ToARGBRow_NEON(const uint8* src_y,
|
|||
"v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30"
|
||||
);
|
||||
}
|
||||
#endif // HAS_I400TOARGBROW_NEON
|
||||
|
||||
#ifdef HAS_J400TOARGBROW_NEON
|
||||
void J400ToARGBRow_NEON(const uint8* src_y,
|
||||
uint8* dst_argb,
|
||||
int width) {
|
||||
|
@ -496,9 +474,7 @@ void J400ToARGBRow_NEON(const uint8* src_y,
|
|||
: "cc", "memory", "v20", "v21", "v22", "v23"
|
||||
);
|
||||
}
|
||||
#endif // HAS_J400TOARGBROW_NEON
|
||||
|
||||
#ifdef HAS_NV12TOARGBROW_NEON
|
||||
void NV12ToARGBRow_NEON(const uint8* src_y,
|
||||
const uint8* src_uv,
|
||||
uint8* dst_argb,
|
||||
|
@ -526,9 +502,7 @@ void NV12ToARGBRow_NEON(const uint8* src_y,
|
|||
"v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30"
|
||||
);
|
||||
}
|
||||
#endif // HAS_NV12TOARGBROW_NEON
|
||||
|
||||
#ifdef HAS_NV12TOARGBROW_NEON
|
||||
void NV21ToARGBRow_NEON(const uint8* src_y,
|
||||
const uint8* src_vu,
|
||||
uint8* dst_argb,
|
||||
|
@ -556,9 +530,7 @@ void NV21ToARGBRow_NEON(const uint8* src_y,
|
|||
"v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30"
|
||||
);
|
||||
}
|
||||
#endif // HAS_NV12TOARGBROW_NEON
|
||||
|
||||
#ifdef HAS_NV12TORGB565ROW_NEON
|
||||
void NV12ToRGB565Row_NEON(const uint8* src_y,
|
||||
const uint8* src_uv,
|
||||
uint8* dst_rgb565,
|
||||
|
@ -586,14 +558,11 @@ void NV12ToRGB565Row_NEON(const uint8* src_y,
|
|||
"v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30"
|
||||
);
|
||||
}
|
||||
#endif // HAS_NV12TORGB565ROW_NEON
|
||||
|
||||
#ifdef HAS_YUY2TOARGBROW_NEON
|
||||
void YUY2ToARGBRow_NEON(const uint8* src_yuy2,
|
||||
uint8* dst_argb,
|
||||
const struct YuvConstants* yuvconstants,
|
||||
int width) {
|
||||
int64 width64 = (int64)(width);
|
||||
asm volatile (
|
||||
YUVTORGB_SETUP
|
||||
"movi v23.8b, #255 \n"
|
||||
|
@ -606,7 +575,7 @@ void YUY2ToARGBRow_NEON(const uint8* src_yuy2,
|
|||
"b.gt 1b \n"
|
||||
: "+r"(src_yuy2), // %0
|
||||
"+r"(dst_argb), // %1
|
||||
"+r"(width64) // %2
|
||||
"+r"(width) // %2
|
||||
: [kUVToRB]"r"(&yuvconstants->kUVToRB),
|
||||
[kUVToG]"r"(&yuvconstants->kUVToG),
|
||||
[kUVBiasBGR]"r"(&yuvconstants->kUVBiasBGR),
|
||||
|
@ -615,14 +584,11 @@ void YUY2ToARGBRow_NEON(const uint8* src_yuy2,
|
|||
"v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30"
|
||||
);
|
||||
}
|
||||
#endif // HAS_YUY2TOARGBROW_NEON
|
||||
|
||||
#ifdef HAS_UYVYTOARGBROW_NEON
|
||||
void UYVYToARGBRow_NEON(const uint8* src_uyvy,
|
||||
uint8* dst_argb,
|
||||
const struct YuvConstants* yuvconstants,
|
||||
int width) {
|
||||
int64 width64 = (int64)(width);
|
||||
asm volatile (
|
||||
YUVTORGB_SETUP
|
||||
"movi v23.8b, #255 \n"
|
||||
|
@ -635,7 +601,7 @@ void UYVYToARGBRow_NEON(const uint8* src_uyvy,
|
|||
"b.gt 1b \n"
|
||||
: "+r"(src_uyvy), // %0
|
||||
"+r"(dst_argb), // %1
|
||||
"+r"(width64) // %2
|
||||
"+r"(width) // %2
|
||||
: [kUVToRB]"r"(&yuvconstants->kUVToRB),
|
||||
[kUVToG]"r"(&yuvconstants->kUVToG),
|
||||
[kUVBiasBGR]"r"(&yuvconstants->kUVBiasBGR),
|
||||
|
@ -644,10 +610,8 @@ void UYVYToARGBRow_NEON(const uint8* src_uyvy,
|
|||
"v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30"
|
||||
);
|
||||
}
|
||||
#endif // HAS_UYVYTOARGBROW_NEON
|
||||
|
||||
// Reads 16 pairs of UV and write even values to dst_u and odd to dst_v.
|
||||
#ifdef HAS_SPLITUVROW_NEON
|
||||
void SplitUVRow_NEON(const uint8* src_uv, uint8* dst_u, uint8* dst_v,
|
||||
int width) {
|
||||
asm volatile (
|
||||
|
@ -668,10 +632,8 @@ void SplitUVRow_NEON(const uint8* src_uv, uint8* dst_u, uint8* dst_v,
|
|||
: "cc", "memory", "v0", "v1" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_SPLITUVROW_NEON
|
||||
|
||||
// Reads 16 U's and V's and writes out 16 pairs of UV.
|
||||
#ifdef HAS_MERGEUVROW_NEON
|
||||
void MergeUVRow_NEON(const uint8* src_u, const uint8* src_v, uint8* dst_uv,
|
||||
int width) {
|
||||
asm volatile (
|
||||
|
@ -693,10 +655,8 @@ void MergeUVRow_NEON(const uint8* src_u, const uint8* src_v, uint8* dst_uv,
|
|||
: "cc", "memory", "v0", "v1" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_MERGEUVROW_NEON
|
||||
|
||||
// Copy multiple of 32. vld4.8 allow unaligned and is fastest on a15.
|
||||
#ifdef HAS_COPYROW_NEON
|
||||
void CopyRow_NEON(const uint8* src, uint8* dst, int count) {
|
||||
asm volatile (
|
||||
"1: \n"
|
||||
|
@ -713,17 +673,16 @@ void CopyRow_NEON(const uint8* src, uint8* dst, int count) {
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_COPYROW_NEON
|
||||
|
||||
// SetRow writes 'count' bytes using an 8 bit value repeated.
|
||||
void SetRow_NEON(uint8* dst, uint8 v8, int count) {
|
||||
asm volatile (
|
||||
"dup v0.16b, %w2 \n" // duplicate 16 bytes
|
||||
"1: \n"
|
||||
"subs %w1, %w1, #16 \n" // 16 bytes per loop
|
||||
"subs %w1, %w1, #16 \n" // 16 bytes per loop
|
||||
MEMACCESS(0)
|
||||
"st1 {v0.16b}, [%0], #16 \n" // store
|
||||
"b.gt 1b \n"
|
||||
"b.gt 1b \n"
|
||||
: "+r"(dst), // %0
|
||||
"+r"(count) // %1
|
||||
: "r"(v8) // %2
|
||||
|
@ -735,10 +694,10 @@ void ARGBSetRow_NEON(uint8* dst, uint32 v32, int count) {
|
|||
asm volatile (
|
||||
"dup v0.4s, %w2 \n" // duplicate 4 ints
|
||||
"1: \n"
|
||||
"subs %w1, %w1, #4 \n" // 4 ints per loop
|
||||
"subs %w1, %w1, #4 \n" // 4 ints per loop
|
||||
MEMACCESS(0)
|
||||
"st1 {v0.16b}, [%0], #16 \n" // store
|
||||
"b.gt 1b \n"
|
||||
"b.gt 1b \n"
|
||||
: "+r"(dst), // %0
|
||||
"+r"(count) // %1
|
||||
: "r"(v32) // %2
|
||||
|
@ -746,18 +705,15 @@ void ARGBSetRow_NEON(uint8* dst, uint32 v32, int count) {
|
|||
);
|
||||
}
|
||||
|
||||
#ifdef HAS_MIRRORROW_NEON
|
||||
void MirrorRow_NEON(const uint8* src, uint8* dst, int width) {
|
||||
int64 width64 = (int64) width;
|
||||
asm volatile (
|
||||
// Start at end of source row.
|
||||
"add %0, %0, %2 \n"
|
||||
"add %0, %0, %w2, sxtw \n"
|
||||
"sub %0, %0, #16 \n"
|
||||
|
||||
"1: \n"
|
||||
MEMACCESS(0)
|
||||
"ld1 {v0.16b}, [%0], %3 \n" // src -= 16
|
||||
"subs %2, %2, #16 \n" // 16 pixels per loop.
|
||||
"subs %w2, %w2, #16 \n" // 16 pixels per loop.
|
||||
"rev64 v0.16b, v0.16b \n"
|
||||
MEMACCESS(1)
|
||||
"st1 {v0.D}[1], [%1], #8 \n" // dst += 16
|
||||
|
@ -766,26 +722,22 @@ void MirrorRow_NEON(const uint8* src, uint8* dst, int width) {
|
|||
"b.gt 1b \n"
|
||||
: "+r"(src), // %0
|
||||
"+r"(dst), // %1
|
||||
"+r"(width64) // %2
|
||||
"+r"(width) // %2
|
||||
: "r"((ptrdiff_t)-16) // %3
|
||||
: "cc", "memory", "v0"
|
||||
);
|
||||
}
|
||||
#endif // HAS_MIRRORROW_NEON
|
||||
|
||||
#ifdef HAS_MIRRORUVROW_NEON
|
||||
void MirrorUVRow_NEON(const uint8* src_uv, uint8* dst_u, uint8* dst_v,
|
||||
int width) {
|
||||
int64 width64 = (int64) width;
|
||||
asm volatile (
|
||||
// Start at end of source row.
|
||||
"add %0, %0, %3, lsl #1 \n"
|
||||
"add %0, %0, %w3, sxtw #1 \n"
|
||||
"sub %0, %0, #16 \n"
|
||||
|
||||
"1: \n"
|
||||
MEMACCESS(0)
|
||||
"ld2 {v0.8b, v1.8b}, [%0], %4 \n" // src -= 16
|
||||
"subs %3, %3, #8 \n" // 8 pixels per loop.
|
||||
"subs %w3, %w3, #8 \n" // 8 pixels per loop.
|
||||
"rev64 v0.8b, v0.8b \n"
|
||||
"rev64 v1.8b, v1.8b \n"
|
||||
MEMACCESS(1)
|
||||
|
@ -796,25 +748,21 @@ void MirrorUVRow_NEON(const uint8* src_uv, uint8* dst_u, uint8* dst_v,
|
|||
: "+r"(src_uv), // %0
|
||||
"+r"(dst_u), // %1
|
||||
"+r"(dst_v), // %2
|
||||
"+r"(width64) // %3
|
||||
"+r"(width) // %3
|
||||
: "r"((ptrdiff_t)-16) // %4
|
||||
: "cc", "memory", "v0", "v1"
|
||||
);
|
||||
}
|
||||
#endif // HAS_MIRRORUVROW_NEON
|
||||
|
||||
#ifdef HAS_ARGBMIRRORROW_NEON
|
||||
void ARGBMirrorRow_NEON(const uint8* src, uint8* dst, int width) {
|
||||
int64 width64 = (int64) width;
|
||||
asm volatile (
|
||||
// Start at end of source row.
|
||||
"add %0, %0, %2, lsl #2 \n"
|
||||
// Start at end of source row.
|
||||
"add %0, %0, %w2, sxtw #2 \n"
|
||||
"sub %0, %0, #16 \n"
|
||||
|
||||
"1: \n"
|
||||
MEMACCESS(0)
|
||||
"ld1 {v0.16b}, [%0], %3 \n" // src -= 16
|
||||
"subs %2, %2, #4 \n" // 4 pixels per loop.
|
||||
"subs %w2, %w2, #4 \n" // 4 pixels per loop.
|
||||
"rev64 v0.4s, v0.4s \n"
|
||||
MEMACCESS(1)
|
||||
"st1 {v0.D}[1], [%1], #8 \n" // dst += 16
|
||||
|
@ -823,14 +771,12 @@ void ARGBMirrorRow_NEON(const uint8* src, uint8* dst, int width) {
|
|||
"b.gt 1b \n"
|
||||
: "+r"(src), // %0
|
||||
"+r"(dst), // %1
|
||||
"+r"(width64) // %2
|
||||
"+r"(width) // %2
|
||||
: "r"((ptrdiff_t)-16) // %3
|
||||
: "cc", "memory", "v0"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBMIRRORROW_NEON
|
||||
|
||||
#ifdef HAS_RGB24TOARGBROW_NEON
|
||||
void RGB24ToARGBRow_NEON(const uint8* src_rgb24, uint8* dst_argb, int width) {
|
||||
asm volatile (
|
||||
"movi v4.8b, #255 \n" // Alpha
|
||||
|
@ -843,14 +789,12 @@ void RGB24ToARGBRow_NEON(const uint8* src_rgb24, uint8* dst_argb, int width) {
|
|||
"b.gt 1b \n"
|
||||
: "+r"(src_rgb24), // %0
|
||||
"+r"(dst_argb), // %1
|
||||
"+r"(width) // %2
|
||||
"+r"(width) // %2
|
||||
:
|
||||
: "cc", "memory", "v1", "v2", "v3", "v4" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_RGB24TOARGBROW_NEON
|
||||
|
||||
#ifdef HAS_RAWTOARGBROW_NEON
|
||||
void RAWToARGBRow_NEON(const uint8* src_raw, uint8* dst_argb, int width) {
|
||||
asm volatile (
|
||||
"movi v5.8b, #255 \n" // Alpha
|
||||
|
@ -865,12 +809,11 @@ void RAWToARGBRow_NEON(const uint8* src_raw, uint8* dst_argb, int width) {
|
|||
"b.gt 1b \n"
|
||||
: "+r"(src_raw), // %0
|
||||
"+r"(dst_argb), // %1
|
||||
"+r"(width) // %2
|
||||
"+r"(width) // %2
|
||||
:
|
||||
: "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_RAWTOARGBROW_NEON
|
||||
|
||||
void RAWToRGB24Row_NEON(const uint8* src_raw, uint8* dst_rgb24, int width) {
|
||||
asm volatile (
|
||||
|
@ -904,7 +847,6 @@ void RAWToRGB24Row_NEON(const uint8* src_raw, uint8* dst_rgb24, int width) {
|
|||
"orr v0.16b, v0.16b, v2.16b \n" /* R,B */ \
|
||||
"dup v2.2D, v0.D[1] \n" /* R */
|
||||
|
||||
#ifdef HAS_RGB565TOARGBROW_NEON
|
||||
void RGB565ToARGBRow_NEON(const uint8* src_rgb565, uint8* dst_argb, int width) {
|
||||
asm volatile (
|
||||
"movi v3.8b, #255 \n" // Alpha
|
||||
|
@ -923,7 +865,6 @@ void RGB565ToARGBRow_NEON(const uint8* src_rgb565, uint8* dst_argb, int width) {
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v6" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_RGB565TOARGBROW_NEON
|
||||
|
||||
#define ARGB1555TOARGB \
|
||||
"ushr v2.8h, v0.8h, #10 \n" /* R xxxRRRRR */ \
|
||||
|
@ -962,7 +903,6 @@ void RGB565ToARGBRow_NEON(const uint8* src_rgb565, uint8* dst_argb, int width) {
|
|||
"orr v2.16b, v1.16b, v3.16b \n" /* R */ \
|
||||
"dup v1.2D, v0.D[1] \n" /* G */ \
|
||||
|
||||
#ifdef HAS_ARGB1555TOARGBROW_NEON
|
||||
void ARGB1555ToARGBRow_NEON(const uint8* src_argb1555, uint8* dst_argb,
|
||||
int width) {
|
||||
asm volatile (
|
||||
|
@ -982,7 +922,6 @@ void ARGB1555ToARGBRow_NEON(const uint8* src_argb1555, uint8* dst_argb,
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGB1555TOARGBROW_NEON
|
||||
|
||||
#define ARGB4444TOARGB \
|
||||
"shrn v1.8b, v0.8h, #8 \n" /* v1(l) AR */ \
|
||||
|
@ -996,7 +935,6 @@ void ARGB1555ToARGBRow_NEON(const uint8* src_argb1555, uint8* dst_argb,
|
|||
"dup v0.2D, v2.D[1] \n" \
|
||||
"dup v1.2D, v3.D[1] \n"
|
||||
|
||||
#ifdef HAS_ARGB4444TOARGBROW_NEON
|
||||
void ARGB4444ToARGBRow_NEON(const uint8* src_argb4444, uint8* dst_argb,
|
||||
int width) {
|
||||
asm volatile (
|
||||
|
@ -1015,9 +953,7 @@ void ARGB4444ToARGBRow_NEON(const uint8* src_argb4444, uint8* dst_argb,
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3", "v4" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGB4444TOARGBROW_NEON
|
||||
|
||||
#ifdef HAS_ARGBTORGB24ROW_NEON
|
||||
void ARGBToRGB24Row_NEON(const uint8* src_argb, uint8* dst_rgb24, int width) {
|
||||
asm volatile (
|
||||
"1: \n"
|
||||
|
@ -1034,9 +970,7 @@ void ARGBToRGB24Row_NEON(const uint8* src_argb, uint8* dst_rgb24, int width) {
|
|||
: "cc", "memory", "v1", "v2", "v3", "v4" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBTORGB24ROW_NEON
|
||||
|
||||
#ifdef HAS_ARGBTORAWROW_NEON
|
||||
void ARGBToRAWRow_NEON(const uint8* src_argb, uint8* dst_raw, int width) {
|
||||
asm volatile (
|
||||
"1: \n"
|
||||
|
@ -1055,9 +989,7 @@ void ARGBToRAWRow_NEON(const uint8* src_argb, uint8* dst_raw, int width) {
|
|||
: "cc", "memory", "v1", "v2", "v3", "v4", "v5" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBTORAWROW_NEON
|
||||
|
||||
#ifdef HAS_YUY2TOYROW_NEON
|
||||
void YUY2ToYRow_NEON(const uint8* src_yuy2, uint8* dst_y, int width) {
|
||||
asm volatile (
|
||||
"1: \n"
|
||||
|
@ -1074,9 +1006,7 @@ void YUY2ToYRow_NEON(const uint8* src_yuy2, uint8* dst_y, int width) {
|
|||
: "cc", "memory", "v0", "v1" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_YUY2TOYROW_NEON
|
||||
|
||||
#ifdef HAS_UYVYTOYROW_NEON
|
||||
void UYVYToYRow_NEON(const uint8* src_uyvy, uint8* dst_y, int width) {
|
||||
asm volatile (
|
||||
"1: \n"
|
||||
|
@ -1093,9 +1023,7 @@ void UYVYToYRow_NEON(const uint8* src_uyvy, uint8* dst_y, int width) {
|
|||
: "cc", "memory", "v0", "v1" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_UYVYTOYROW_NEON
|
||||
|
||||
#ifdef HAS_YUY2TOUV422ROW_NEON
|
||||
void YUY2ToUV422Row_NEON(const uint8* src_yuy2, uint8* dst_u, uint8* dst_v,
|
||||
int width) {
|
||||
asm volatile (
|
||||
|
@ -1116,9 +1044,7 @@ void YUY2ToUV422Row_NEON(const uint8* src_yuy2, uint8* dst_u, uint8* dst_v,
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_YUY2TOUV422ROW_NEON
|
||||
|
||||
#ifdef HAS_UYVYTOUV422ROW_NEON
|
||||
void UYVYToUV422Row_NEON(const uint8* src_uyvy, uint8* dst_u, uint8* dst_v,
|
||||
int width) {
|
||||
asm volatile (
|
||||
|
@ -1139,9 +1065,7 @@ void UYVYToUV422Row_NEON(const uint8* src_uyvy, uint8* dst_u, uint8* dst_v,
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_UYVYTOUV422ROW_NEON
|
||||
|
||||
#ifdef HAS_YUY2TOUVROW_NEON
|
||||
void YUY2ToUVRow_NEON(const uint8* src_yuy2, int stride_yuy2,
|
||||
uint8* dst_u, uint8* dst_v, int width) {
|
||||
const uint8* src_yuy2b = src_yuy2 + stride_yuy2;
|
||||
|
@ -1169,9 +1093,7 @@ void YUY2ToUVRow_NEON(const uint8* src_yuy2, int stride_yuy2,
|
|||
"v5", "v6", "v7" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_YUY2TOUVROW_NEON
|
||||
|
||||
#ifdef HAS_UYVYTOUVROW_NEON
|
||||
void UYVYToUVRow_NEON(const uint8* src_uyvy, int stride_uyvy,
|
||||
uint8* dst_u, uint8* dst_v, int width) {
|
||||
const uint8* src_uyvyb = src_uyvy + stride_uyvy;
|
||||
|
@ -1199,10 +1121,8 @@ void UYVYToUVRow_NEON(const uint8* src_uyvy, int stride_uyvy,
|
|||
"v5", "v6", "v7" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_UYVYTOUVROW_NEON
|
||||
|
||||
// For BGRAToARGB, ABGRToARGB, RGBAToARGB, and ARGBToRGBA.
|
||||
#ifdef HAS_ARGBSHUFFLEROW_NEON
|
||||
void ARGBShuffleRow_NEON(const uint8* src_argb, uint8* dst_argb,
|
||||
const uint8* shuffler, int width) {
|
||||
asm volatile (
|
||||
|
@ -1223,9 +1143,7 @@ void ARGBShuffleRow_NEON(const uint8* src_argb, uint8* dst_argb,
|
|||
: "cc", "memory", "v0", "v1", "v2" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBSHUFFLEROW_NEON
|
||||
|
||||
#ifdef HAS_I422TOYUY2ROW_NEON
|
||||
void I422ToYUY2Row_NEON(const uint8* src_y,
|
||||
const uint8* src_u,
|
||||
const uint8* src_v,
|
||||
|
@ -1252,9 +1170,7 @@ void I422ToYUY2Row_NEON(const uint8* src_y,
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3"
|
||||
);
|
||||
}
|
||||
#endif // HAS_I422TOYUY2ROW_NEON
|
||||
|
||||
#ifdef HAS_I422TOUYVYROW_NEON
|
||||
void I422ToUYVYRow_NEON(const uint8* src_y,
|
||||
const uint8* src_u,
|
||||
const uint8* src_v,
|
||||
|
@ -1281,9 +1197,7 @@ void I422ToUYVYRow_NEON(const uint8* src_y,
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3"
|
||||
);
|
||||
}
|
||||
#endif // HAS_I422TOUYVYROW_NEON
|
||||
|
||||
#ifdef HAS_ARGBTORGB565ROW_NEON
|
||||
void ARGBToRGB565Row_NEON(const uint8* src_argb, uint8* dst_rgb565, int width) {
|
||||
asm volatile (
|
||||
"1: \n"
|
||||
|
@ -1301,9 +1215,7 @@ void ARGBToRGB565Row_NEON(const uint8* src_argb, uint8* dst_rgb565, int width) {
|
|||
: "cc", "memory", "v0", "v20", "v21", "v22", "v23"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBTORGB565ROW_NEON
|
||||
|
||||
#ifdef HAS_ARGBTORGB565DITHERROW_NEON
|
||||
void ARGBToRGB565DitherRow_NEON(const uint8* src_argb, uint8* dst_rgb,
|
||||
const uint32 dither4, int width) {
|
||||
asm volatile (
|
||||
|
@ -1326,9 +1238,7 @@ void ARGBToRGB565DitherRow_NEON(const uint8* src_argb, uint8* dst_rgb,
|
|||
: "cc", "memory", "v0", "v1", "v20", "v21", "v22", "v23"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBTORGB565ROW_NEON
|
||||
|
||||
#ifdef HAS_ARGBTOARGB1555ROW_NEON
|
||||
void ARGBToARGB1555Row_NEON(const uint8* src_argb, uint8* dst_argb1555,
|
||||
int width) {
|
||||
asm volatile (
|
||||
|
@ -1347,9 +1257,7 @@ void ARGBToARGB1555Row_NEON(const uint8* src_argb, uint8* dst_argb1555,
|
|||
: "cc", "memory", "v0", "v20", "v21", "v22", "v23"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBTOARGB1555ROW_NEON
|
||||
|
||||
#ifdef HAS_ARGBTOARGB4444ROW_NEON
|
||||
void ARGBToARGB4444Row_NEON(const uint8* src_argb, uint8* dst_argb4444,
|
||||
int width) {
|
||||
asm volatile (
|
||||
|
@ -1369,9 +1277,7 @@ void ARGBToARGB4444Row_NEON(const uint8* src_argb, uint8* dst_argb4444,
|
|||
: "cc", "memory", "v0", "v1", "v4", "v20", "v21", "v22", "v23"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBTOARGB4444ROW_NEON
|
||||
|
||||
#ifdef HAS_ARGBTOYROW_NEON
|
||||
void ARGBToYRow_NEON(const uint8* src_argb, uint8* dst_y, int width) {
|
||||
asm volatile (
|
||||
"movi v4.8b, #13 \n" // B * 0.1016 coefficient
|
||||
|
@ -1397,9 +1303,24 @@ void ARGBToYRow_NEON(const uint8* src_argb, uint8* dst_y, int width) {
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBTOYROW_NEON
|
||||
|
||||
#ifdef HAS_ARGBTOYJROW_NEON
|
||||
void ARGBExtractAlphaRow_NEON(const uint8* src_argb, uint8* dst_a, int width) {
|
||||
asm volatile (
|
||||
"1: \n"
|
||||
MEMACCESS(0)
|
||||
"ld4 {v0.16b,v1.16b,v2.16b,v3.16b}, [%0], #64 \n" // load row 16 pixels
|
||||
"subs %w2, %w2, #16 \n" // 16 processed per loop
|
||||
MEMACCESS(1)
|
||||
"st1 {v3.16b}, [%1], #16 \n" // store 16 A's.
|
||||
"b.gt 1b \n"
|
||||
: "+r"(src_argb), // %0
|
||||
"+r"(dst_a), // %1
|
||||
"+r"(width) // %2
|
||||
:
|
||||
: "cc", "memory", "v0", "v1", "v2", "v3" // Clobber List
|
||||
);
|
||||
}
|
||||
|
||||
void ARGBToYJRow_NEON(const uint8* src_argb, uint8* dst_y, int width) {
|
||||
asm volatile (
|
||||
"movi v4.8b, #15 \n" // B * 0.11400 coefficient
|
||||
|
@ -1423,10 +1344,8 @@ void ARGBToYJRow_NEON(const uint8* src_argb, uint8* dst_y, int width) {
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBTOYJROW_NEON
|
||||
|
||||
// 8x1 pixels.
|
||||
#ifdef HAS_ARGBTOUV444ROW_NEON
|
||||
void ARGBToUV444Row_NEON(const uint8* src_argb, uint8* dst_u, uint8* dst_v,
|
||||
int width) {
|
||||
asm volatile (
|
||||
|
@ -1467,7 +1386,6 @@ void ARGBToUV444Row_NEON(const uint8* src_argb, uint8* dst_u, uint8* dst_v,
|
|||
"v24", "v25", "v26", "v27", "v28", "v29"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBTOUV444ROW_NEON
|
||||
|
||||
#define RGBTOUV_SETUP_REG \
|
||||
"movi v20.8h, #56, lsl #0 \n" /* UB/VR coefficient (0.875) / 2 */ \
|
||||
|
@ -1478,7 +1396,6 @@ void ARGBToUV444Row_NEON(const uint8* src_argb, uint8* dst_u, uint8* dst_v,
|
|||
"movi v25.16b, #0x80 \n" /* 128.5 (0x8080 in 16-bit) */
|
||||
|
||||
// 32x1 pixels -> 8x1. width is number of argb pixels. e.g. 32.
|
||||
#ifdef HAS_ARGBTOUV411ROW_NEON
|
||||
void ARGBToUV411Row_NEON(const uint8* src_argb, uint8* dst_u, uint8* dst_v,
|
||||
int width) {
|
||||
asm volatile (
|
||||
|
@ -1528,7 +1445,6 @@ void ARGBToUV411Row_NEON(const uint8* src_argb, uint8* dst_u, uint8* dst_v,
|
|||
"v20", "v21", "v22", "v23", "v24", "v25"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBTOUV411ROW_NEON
|
||||
|
||||
// 16x2 pixels -> 8x1. width is number of argb pixels. e.g. 16.
|
||||
#define RGBTOUV(QB, QG, QR) \
|
||||
|
@ -1546,7 +1462,6 @@ void ARGBToUV411Row_NEON(const uint8* src_argb, uint8* dst_u, uint8* dst_v,
|
|||
// TODO(fbarchard): Consider vhadd vertical, then vpaddl horizontal, avoid shr.
|
||||
// TODO(fbarchard): consider ptrdiff_t for all strides.
|
||||
|
||||
#ifdef HAS_ARGBTOUVROW_NEON
|
||||
void ARGBToUVRow_NEON(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_u, uint8* dst_v, int width) {
|
||||
const uint8* src_argb_1 = src_argb + src_stride_argb;
|
||||
|
@ -1586,10 +1501,8 @@ void ARGBToUVRow_NEON(const uint8* src_argb, int src_stride_argb,
|
|||
"v20", "v21", "v22", "v23", "v24", "v25"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBTOUVROW_NEON
|
||||
|
||||
// TODO(fbarchard): Subsample match C code.
|
||||
#ifdef HAS_ARGBTOUVJROW_NEON
|
||||
void ARGBToUVJRow_NEON(const uint8* src_argb, int src_stride_argb,
|
||||
uint8* dst_u, uint8* dst_v, int width) {
|
||||
const uint8* src_argb_1 = src_argb + src_stride_argb;
|
||||
|
@ -1633,9 +1546,7 @@ void ARGBToUVJRow_NEON(const uint8* src_argb, int src_stride_argb,
|
|||
"v20", "v21", "v22", "v23", "v24", "v25"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBTOUVJROW_NEON
|
||||
|
||||
#ifdef HAS_BGRATOUVROW_NEON
|
||||
void BGRAToUVRow_NEON(const uint8* src_bgra, int src_stride_bgra,
|
||||
uint8* dst_u, uint8* dst_v, int width) {
|
||||
const uint8* src_bgra_1 = src_bgra + src_stride_bgra;
|
||||
|
@ -1674,9 +1585,7 @@ void BGRAToUVRow_NEON(const uint8* src_bgra, int src_stride_bgra,
|
|||
"v20", "v21", "v22", "v23", "v24", "v25"
|
||||
);
|
||||
}
|
||||
#endif // HAS_BGRATOUVROW_NEON
|
||||
|
||||
#ifdef HAS_ABGRTOUVROW_NEON
|
||||
void ABGRToUVRow_NEON(const uint8* src_abgr, int src_stride_abgr,
|
||||
uint8* dst_u, uint8* dst_v, int width) {
|
||||
const uint8* src_abgr_1 = src_abgr + src_stride_abgr;
|
||||
|
@ -1715,9 +1624,7 @@ void ABGRToUVRow_NEON(const uint8* src_abgr, int src_stride_abgr,
|
|||
"v20", "v21", "v22", "v23", "v24", "v25"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ABGRTOUVROW_NEON
|
||||
|
||||
#ifdef HAS_RGBATOUVROW_NEON
|
||||
void RGBAToUVRow_NEON(const uint8* src_rgba, int src_stride_rgba,
|
||||
uint8* dst_u, uint8* dst_v, int width) {
|
||||
const uint8* src_rgba_1 = src_rgba + src_stride_rgba;
|
||||
|
@ -1756,9 +1663,7 @@ void RGBAToUVRow_NEON(const uint8* src_rgba, int src_stride_rgba,
|
|||
"v20", "v21", "v22", "v23", "v24", "v25"
|
||||
);
|
||||
}
|
||||
#endif // HAS_RGBATOUVROW_NEON
|
||||
|
||||
#ifdef HAS_RGB24TOUVROW_NEON
|
||||
void RGB24ToUVRow_NEON(const uint8* src_rgb24, int src_stride_rgb24,
|
||||
uint8* dst_u, uint8* dst_v, int width) {
|
||||
const uint8* src_rgb24_1 = src_rgb24 + src_stride_rgb24;
|
||||
|
@ -1797,9 +1702,7 @@ void RGB24ToUVRow_NEON(const uint8* src_rgb24, int src_stride_rgb24,
|
|||
"v20", "v21", "v22", "v23", "v24", "v25"
|
||||
);
|
||||
}
|
||||
#endif // HAS_RGB24TOUVROW_NEON
|
||||
|
||||
#ifdef HAS_RAWTOUVROW_NEON
|
||||
void RAWToUVRow_NEON(const uint8* src_raw, int src_stride_raw,
|
||||
uint8* dst_u, uint8* dst_v, int width) {
|
||||
const uint8* src_raw_1 = src_raw + src_stride_raw;
|
||||
|
@ -1838,10 +1741,8 @@ void RAWToUVRow_NEON(const uint8* src_raw, int src_stride_raw,
|
|||
"v20", "v21", "v22", "v23", "v24", "v25"
|
||||
);
|
||||
}
|
||||
#endif // HAS_RAWTOUVROW_NEON
|
||||
|
||||
// 16x2 pixels -> 8x1. width is number of argb pixels. e.g. 16.
|
||||
#ifdef HAS_RGB565TOUVROW_NEON
|
||||
void RGB565ToUVRow_NEON(const uint8* src_rgb565, int src_stride_rgb565,
|
||||
uint8* dst_u, uint8* dst_v, int width) {
|
||||
const uint8* src_rgb565_1 = src_rgb565 + src_stride_rgb565;
|
||||
|
@ -1914,10 +1815,8 @@ void RGB565ToUVRow_NEON(const uint8* src_rgb565, int src_stride_rgb565,
|
|||
"v25", "v26", "v27"
|
||||
);
|
||||
}
|
||||
#endif // HAS_RGB565TOUVROW_NEON
|
||||
|
||||
// 16x2 pixels -> 8x1. width is number of argb pixels. e.g. 16.
|
||||
#ifdef HAS_ARGB1555TOUVROW_NEON
|
||||
void ARGB1555ToUVRow_NEON(const uint8* src_argb1555, int src_stride_argb1555,
|
||||
uint8* dst_u, uint8* dst_v, int width) {
|
||||
const uint8* src_argb1555_1 = src_argb1555 + src_stride_argb1555;
|
||||
|
@ -1985,10 +1884,8 @@ void ARGB1555ToUVRow_NEON(const uint8* src_argb1555, int src_stride_argb1555,
|
|||
"v26", "v27", "v28"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGB1555TOUVROW_NEON
|
||||
|
||||
// 16x2 pixels -> 8x1. width is number of argb pixels. e.g. 16.
|
||||
#ifdef HAS_ARGB4444TOUVROW_NEON
|
||||
void ARGB4444ToUVRow_NEON(const uint8* src_argb4444, int src_stride_argb4444,
|
||||
uint8* dst_u, uint8* dst_v, int width) {
|
||||
const uint8* src_argb4444_1 = src_argb4444 + src_stride_argb4444;
|
||||
|
@ -2057,9 +1954,7 @@ void ARGB4444ToUVRow_NEON(const uint8* src_argb4444, int src_stride_argb4444,
|
|||
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGB4444TOUVROW_NEON
|
||||
|
||||
#ifdef HAS_RGB565TOYROW_NEON
|
||||
void RGB565ToYRow_NEON(const uint8* src_rgb565, uint8* dst_y, int width) {
|
||||
asm volatile (
|
||||
"movi v24.8b, #13 \n" // B * 0.1016 coefficient
|
||||
|
@ -2087,9 +1982,7 @@ void RGB565ToYRow_NEON(const uint8* src_rgb565, uint8* dst_y, int width) {
|
|||
"v24", "v25", "v26", "v27"
|
||||
);
|
||||
}
|
||||
#endif // HAS_RGB565TOYROW_NEON
|
||||
|
||||
#ifdef HAS_ARGB1555TOYROW_NEON
|
||||
void ARGB1555ToYRow_NEON(const uint8* src_argb1555, uint8* dst_y, int width) {
|
||||
asm volatile (
|
||||
"movi v4.8b, #13 \n" // B * 0.1016 coefficient
|
||||
|
@ -2116,9 +2009,7 @@ void ARGB1555ToYRow_NEON(const uint8* src_argb1555, uint8* dst_y, int width) {
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGB1555TOYROW_NEON
|
||||
|
||||
#ifdef HAS_ARGB4444TOYROW_NEON
|
||||
void ARGB4444ToYRow_NEON(const uint8* src_argb4444, uint8* dst_y, int width) {
|
||||
asm volatile (
|
||||
"movi v24.8b, #13 \n" // B * 0.1016 coefficient
|
||||
|
@ -2145,9 +2036,7 @@ void ARGB4444ToYRow_NEON(const uint8* src_argb4444, uint8* dst_y, int width) {
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3", "v24", "v25", "v26", "v27"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGB4444TOYROW_NEON
|
||||
|
||||
#ifdef HAS_BGRATOYROW_NEON
|
||||
void BGRAToYRow_NEON(const uint8* src_bgra, uint8* dst_y, int width) {
|
||||
asm volatile (
|
||||
"movi v4.8b, #33 \n" // R * 0.2578 coefficient
|
||||
|
@ -2173,9 +2062,7 @@ void BGRAToYRow_NEON(const uint8* src_bgra, uint8* dst_y, int width) {
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v16"
|
||||
);
|
||||
}
|
||||
#endif // HAS_BGRATOYROW_NEON
|
||||
|
||||
#ifdef HAS_ABGRTOYROW_NEON
|
||||
void ABGRToYRow_NEON(const uint8* src_abgr, uint8* dst_y, int width) {
|
||||
asm volatile (
|
||||
"movi v4.8b, #33 \n" // R * 0.2578 coefficient
|
||||
|
@ -2201,9 +2088,7 @@ void ABGRToYRow_NEON(const uint8* src_abgr, uint8* dst_y, int width) {
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v16"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ABGRTOYROW_NEON
|
||||
|
||||
#ifdef HAS_RGBATOYROW_NEON
|
||||
void RGBAToYRow_NEON(const uint8* src_rgba, uint8* dst_y, int width) {
|
||||
asm volatile (
|
||||
"movi v4.8b, #13 \n" // B * 0.1016 coefficient
|
||||
|
@ -2229,9 +2114,7 @@ void RGBAToYRow_NEON(const uint8* src_rgba, uint8* dst_y, int width) {
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v16"
|
||||
);
|
||||
}
|
||||
#endif // HAS_RGBATOYROW_NEON
|
||||
|
||||
#ifdef HAS_RGB24TOYROW_NEON
|
||||
void RGB24ToYRow_NEON(const uint8* src_rgb24, uint8* dst_y, int width) {
|
||||
asm volatile (
|
||||
"movi v4.8b, #13 \n" // B * 0.1016 coefficient
|
||||
|
@ -2257,9 +2140,7 @@ void RGB24ToYRow_NEON(const uint8* src_rgb24, uint8* dst_y, int width) {
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v16"
|
||||
);
|
||||
}
|
||||
#endif // HAS_RGB24TOYROW_NEON
|
||||
|
||||
#ifdef HAS_RAWTOYROW_NEON
|
||||
void RAWToYRow_NEON(const uint8* src_raw, uint8* dst_y, int width) {
|
||||
asm volatile (
|
||||
"movi v4.8b, #33 \n" // R * 0.2578 coefficient
|
||||
|
@ -2285,10 +2166,8 @@ void RAWToYRow_NEON(const uint8* src_raw, uint8* dst_y, int width) {
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v16"
|
||||
);
|
||||
}
|
||||
#endif // HAS_RAWTOYROW_NEON
|
||||
|
||||
// Bilinear filter 16x2 -> 16x1
|
||||
#ifdef HAS_INTERPOLATEROW_NEON
|
||||
void InterpolateRow_NEON(uint8* dst_ptr,
|
||||
const uint8* src_ptr, ptrdiff_t src_stride,
|
||||
int dst_width, int source_y_fraction) {
|
||||
|
@ -2354,10 +2233,8 @@ void InterpolateRow_NEON(uint8* dst_ptr,
|
|||
: "cc", "memory", "v0", "v1", "v3", "v4", "v5"
|
||||
);
|
||||
}
|
||||
#endif // HAS_INTERPOLATEROW_NEON
|
||||
|
||||
// dr * (256 - sa) / 256 + sr = dr - dr * sa / 256 + sr
|
||||
#ifdef HAS_ARGBBLENDROW_NEON
|
||||
void ARGBBlendRow_NEON(const uint8* src_argb0, const uint8* src_argb1,
|
||||
uint8* dst_argb, int width) {
|
||||
asm volatile (
|
||||
|
@ -2426,10 +2303,8 @@ void ARGBBlendRow_NEON(const uint8* src_argb0, const uint8* src_argb1,
|
|||
"v16", "v17", "v18"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBBLENDROW_NEON
|
||||
|
||||
// Attenuate 8 pixels at a time.
|
||||
#ifdef HAS_ARGBATTENUATEROW_NEON
|
||||
void ARGBAttenuateRow_NEON(const uint8* src_argb, uint8* dst_argb, int width) {
|
||||
asm volatile (
|
||||
// Attenuate 8 pixels.
|
||||
|
@ -2453,11 +2328,9 @@ void ARGBAttenuateRow_NEON(const uint8* src_argb, uint8* dst_argb, int width) {
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBATTENUATEROW_NEON
|
||||
|
||||
// Quantize 8 ARGB pixels (32 bytes).
|
||||
// dst = (dst * scale >> 16) * interval_size + interval_offset;
|
||||
#ifdef HAS_ARGBQUANTIZEROW_NEON
|
||||
void ARGBQuantizeRow_NEON(uint8* dst_argb, int scale, int interval_size,
|
||||
int interval_offset, int width) {
|
||||
asm volatile (
|
||||
|
@ -2497,12 +2370,10 @@ void ARGBQuantizeRow_NEON(uint8* dst_argb, int scale, int interval_size,
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBQUANTIZEROW_NEON
|
||||
|
||||
// Shade 8 pixels at a time by specified value.
|
||||
// NOTE vqrdmulh.s16 q10, q10, d0[0] must use a scaler register from 0 to 8.
|
||||
// Rounding in vqrdmulh does +1 to high if high bit of low s16 is set.
|
||||
#ifdef HAS_ARGBSHADEROW_NEON
|
||||
void ARGBShadeRow_NEON(const uint8* src_argb, uint8* dst_argb, int width,
|
||||
uint32 value) {
|
||||
asm volatile (
|
||||
|
@ -2537,12 +2408,10 @@ void ARGBShadeRow_NEON(const uint8* src_argb, uint8* dst_argb, int width,
|
|||
: "cc", "memory", "v0", "v4", "v5", "v6", "v7"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBSHADEROW_NEON
|
||||
|
||||
// Convert 8 ARGB pixels (64 bytes) to 8 Gray ARGB pixels
|
||||
// Similar to ARGBToYJ but stores ARGB.
|
||||
// C code is (15 * b + 75 * g + 38 * r + 64) >> 7;
|
||||
#ifdef HAS_ARGBGRAYROW_NEON
|
||||
void ARGBGrayRow_NEON(const uint8* src_argb, uint8* dst_argb, int width) {
|
||||
asm volatile (
|
||||
"movi v24.8b, #15 \n" // B * 0.11400 coefficient
|
||||
|
@ -2568,14 +2437,12 @@ void ARGBGrayRow_NEON(const uint8* src_argb, uint8* dst_argb, int width) {
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v24", "v25", "v26"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBGRAYROW_NEON
|
||||
|
||||
// Convert 8 ARGB pixels (32 bytes) to 8 Sepia ARGB pixels.
|
||||
// b = (r * 35 + g * 68 + b * 17) >> 7
|
||||
// g = (r * 45 + g * 88 + b * 22) >> 7
|
||||
// r = (r * 50 + g * 98 + b * 24) >> 7
|
||||
|
||||
#ifdef HAS_ARGBSEPIAROW_NEON
|
||||
void ARGBSepiaRow_NEON(uint8* dst_argb, int width) {
|
||||
asm volatile (
|
||||
"movi v20.8b, #17 \n" // BB coefficient
|
||||
|
@ -2613,12 +2480,10 @@ void ARGBSepiaRow_NEON(uint8* dst_argb, int width) {
|
|||
"v20", "v21", "v22", "v24", "v25", "v26", "v28", "v29", "v30"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBSEPIAROW_NEON
|
||||
|
||||
// Tranform 8 ARGB pixels (32 bytes) with color matrix.
|
||||
// TODO(fbarchard): Was same as Sepia except matrix is provided. This function
|
||||
// needs to saturate. Consider doing a non-saturating version.
|
||||
#ifdef HAS_ARGBCOLORMATRIXROW_NEON
|
||||
void ARGBColorMatrixRow_NEON(const uint8* src_argb, uint8* dst_argb,
|
||||
const int8* matrix_argb, int width) {
|
||||
asm volatile (
|
||||
|
@ -2678,11 +2543,9 @@ void ARGBColorMatrixRow_NEON(const uint8* src_argb, uint8* dst_argb,
|
|||
"v18", "v19", "v22", "v23", "v24", "v25"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBCOLORMATRIXROW_NEON
|
||||
|
||||
// TODO(fbarchard): fix vqshrun in ARGBMultiplyRow_NEON and reenable.
|
||||
// Multiply 2 rows of ARGB pixels together, 8 pixels at a time.
|
||||
#ifdef HAS_ARGBMULTIPLYROW_NEON
|
||||
void ARGBMultiplyRow_NEON(const uint8* src_argb0, const uint8* src_argb1,
|
||||
uint8* dst_argb, int width) {
|
||||
asm volatile (
|
||||
|
@ -2713,10 +2576,8 @@ void ARGBMultiplyRow_NEON(const uint8* src_argb0, const uint8* src_argb1,
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBMULTIPLYROW_NEON
|
||||
|
||||
// Add 2 rows of ARGB pixels together, 8 pixels at a time.
|
||||
#ifdef HAS_ARGBADDROW_NEON
|
||||
void ARGBAddRow_NEON(const uint8* src_argb0, const uint8* src_argb1,
|
||||
uint8* dst_argb, int width) {
|
||||
asm volatile (
|
||||
|
@ -2743,10 +2604,8 @@ void ARGBAddRow_NEON(const uint8* src_argb0, const uint8* src_argb1,
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBADDROW_NEON
|
||||
|
||||
// Subtract 2 rows of ARGB pixels, 8 pixels at a time.
|
||||
#ifdef HAS_ARGBSUBTRACTROW_NEON
|
||||
void ARGBSubtractRow_NEON(const uint8* src_argb0, const uint8* src_argb1,
|
||||
uint8* dst_argb, int width) {
|
||||
asm volatile (
|
||||
|
@ -2773,14 +2632,12 @@ void ARGBSubtractRow_NEON(const uint8* src_argb0, const uint8* src_argb1,
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7"
|
||||
);
|
||||
}
|
||||
#endif // HAS_ARGBSUBTRACTROW_NEON
|
||||
|
||||
// Adds Sobel X and Sobel Y and stores Sobel into ARGB.
|
||||
// A = 255
|
||||
// R = Sobel
|
||||
// G = Sobel
|
||||
// B = Sobel
|
||||
#ifdef HAS_SOBELROW_NEON
|
||||
void SobelRow_NEON(const uint8* src_sobelx, const uint8* src_sobely,
|
||||
uint8* dst_argb, int width) {
|
||||
asm volatile (
|
||||
|
@ -2806,10 +2663,8 @@ void SobelRow_NEON(const uint8* src_sobelx, const uint8* src_sobely,
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3"
|
||||
);
|
||||
}
|
||||
#endif // HAS_SOBELROW_NEON
|
||||
|
||||
// Adds Sobel X and Sobel Y and stores Sobel into plane.
|
||||
#ifdef HAS_SOBELTOPLANEROW_NEON
|
||||
void SobelToPlaneRow_NEON(const uint8* src_sobelx, const uint8* src_sobely,
|
||||
uint8* dst_y, int width) {
|
||||
asm volatile (
|
||||
|
@ -2832,14 +2687,12 @@ void SobelToPlaneRow_NEON(const uint8* src_sobelx, const uint8* src_sobely,
|
|||
: "cc", "memory", "v0", "v1"
|
||||
);
|
||||
}
|
||||
#endif // HAS_SOBELTOPLANEROW_NEON
|
||||
|
||||
// Mixes Sobel X, Sobel Y and Sobel into ARGB.
|
||||
// A = 255
|
||||
// R = Sobel X
|
||||
// G = Sobel
|
||||
// B = Sobel Y
|
||||
#ifdef HAS_SOBELXYROW_NEON
|
||||
void SobelXYRow_NEON(const uint8* src_sobelx, const uint8* src_sobely,
|
||||
uint8* dst_argb, int width) {
|
||||
asm volatile (
|
||||
|
@ -2863,13 +2716,11 @@ void SobelXYRow_NEON(const uint8* src_sobelx, const uint8* src_sobely,
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3"
|
||||
);
|
||||
}
|
||||
#endif // HAS_SOBELXYROW_NEON
|
||||
|
||||
// SobelX as a matrix is
|
||||
// -1 0 1
|
||||
// -2 0 2
|
||||
// -1 0 1
|
||||
#ifdef HAS_SOBELXROW_NEON
|
||||
void SobelXRow_NEON(const uint8* src_y0, const uint8* src_y1,
|
||||
const uint8* src_y2, uint8* dst_sobelx, int width) {
|
||||
asm volatile (
|
||||
|
@ -2908,13 +2759,11 @@ void SobelXRow_NEON(const uint8* src_y0, const uint8* src_y1,
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_SOBELXROW_NEON
|
||||
|
||||
// SobelY as a matrix is
|
||||
// -1 -2 -1
|
||||
// 0 0 0
|
||||
// 1 2 1
|
||||
#ifdef HAS_SOBELYROW_NEON
|
||||
void SobelYRow_NEON(const uint8* src_y0, const uint8* src_y1,
|
||||
uint8* dst_sobely, int width) {
|
||||
asm volatile (
|
||||
|
@ -2952,7 +2801,6 @@ void SobelYRow_NEON(const uint8* src_y0, const uint8* src_y1,
|
|||
: "cc", "memory", "v0", "v1", "v2", "v3" // Clobber List
|
||||
);
|
||||
}
|
||||
#endif // HAS_SOBELYROW_NEON
|
||||
#endif // !defined(LIBYUV_DISABLE_NEON) && defined(__aarch64__)
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -3532,6 +3532,33 @@ void ARGBCopyAlphaRow_AVX2(const uint8* src, uint8* dst, int width) {
|
|||
}
|
||||
#endif // HAS_ARGBCOPYALPHAROW_AVX2
|
||||
|
||||
#ifdef HAS_ARGBEXTRACTALPHAROW_SSE2
|
||||
// width in pixels
|
||||
__declspec(naked)
|
||||
void ARGBExtractAlphaRow_SSE2(const uint8* src_argb, uint8* dst_a, int width) {
|
||||
__asm {
|
||||
mov eax, [esp + 4] // src_argb
|
||||
mov edx, [esp + 8] // dst_a
|
||||
mov ecx, [esp + 12] // width
|
||||
|
||||
extractloop:
|
||||
movdqu xmm0, [eax]
|
||||
movdqu xmm1, [eax + 16]
|
||||
lea eax, [eax + 32]
|
||||
psrld xmm0, 24
|
||||
psrld xmm1, 24
|
||||
packssdw xmm0, xmm1
|
||||
packuswb xmm0, xmm0
|
||||
movq qword ptr [edx], xmm0
|
||||
lea edx, [edx + 8]
|
||||
sub ecx, 8
|
||||
jg extractloop
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
||||
#endif // HAS_ARGBEXTRACTALPHAROW_SSE2
|
||||
|
||||
#ifdef HAS_ARGBCOPYYTOALPHAROW_SSE2
|
||||
// width in pixels
|
||||
__declspec(naked)
|
||||
|
@ -5248,6 +5275,7 @@ void SobelXYRow_SSE2(const uint8* src_sobelx, const uint8* src_sobely,
|
|||
// dst points to pixel to store result to.
|
||||
// count is number of averaged pixels to produce.
|
||||
// Does 4 pixels at a time.
|
||||
// This function requires alignment on accumulation buffer pointers.
|
||||
void CumulativeSumToAverageRow_SSE2(const int32* topleft, const int32* botleft,
|
||||
int width, int area, uint8* dst,
|
||||
int count) {
|
||||
|
|
|
@ -25,6 +25,7 @@ struct FourCCAliasEntry {
|
|||
|
||||
static const struct FourCCAliasEntry kFourCCAliases[] = {
|
||||
{FOURCC_IYUV, FOURCC_I420},
|
||||
{FOURCC_YU12, FOURCC_I420},
|
||||
{FOURCC_YU16, FOURCC_I422},
|
||||
{FOURCC_YU24, FOURCC_I444},
|
||||
{FOURCC_YUYV, FOURCC_YUY2},
|
||||
|
|
|
@ -231,6 +231,8 @@
|
|||
|
||||
<receiver android:name=".ShareBroadcastReceiver" android:enabled="true"/>
|
||||
|
||||
<receiver android:name=".NotificationDismissReceiver" android:exported="false"/>
|
||||
|
||||
<uses-library android:name="com.sec.android.app.multiwindow" android:required="false" />
|
||||
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
|
||||
<meta-data android:name="com.sec.android.multiwindow.DEFAULT_SIZE_W" android:value="632dp" />
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
package org.telegram.messenger;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
|
@ -48,6 +51,7 @@ import android.view.Surface;
|
|||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.EdgeEffect;
|
||||
import android.widget.EditText;
|
||||
|
@ -61,10 +65,6 @@ import net.hockeyapp.android.UpdateManager;
|
|||
|
||||
import org.telegram.tgnet.ConnectionsManager;
|
||||
import org.telegram.tgnet.TLRPC;
|
||||
import org.telegram.messenger.AnimationCompat.AnimatorListenerAdapterProxy;
|
||||
import org.telegram.messenger.AnimationCompat.AnimatorSetProxy;
|
||||
import org.telegram.messenger.AnimationCompat.ObjectAnimatorProxy;
|
||||
import org.telegram.messenger.AnimationCompat.ViewProxy;
|
||||
import org.telegram.ui.ActionBar.BaseFragment;
|
||||
import org.telegram.ui.Components.ForegroundDetector;
|
||||
import org.telegram.ui.Components.NumberPicker;
|
||||
|
@ -156,11 +156,7 @@ public class AndroidUtilities {
|
|||
}
|
||||
}
|
||||
} else if (drawable instanceof ColorDrawable) {
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
bitmapColor = ((ColorDrawable) drawable).getColor();
|
||||
} else {
|
||||
bitmapColor = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE).getInt("selectedColor", 0xff000000);
|
||||
}
|
||||
bitmapColor = ((ColorDrawable) drawable).getColor();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
|
@ -500,11 +496,7 @@ public class AndroidUtilities {
|
|||
Display display = manager.getDefaultDisplay();
|
||||
if (display != null) {
|
||||
display.getMetrics(displayMetrics);
|
||||
if (android.os.Build.VERSION.SDK_INT < 13) {
|
||||
displaySize.set(display.getWidth(), display.getHeight());
|
||||
} else {
|
||||
display.getSize(displaySize);
|
||||
}
|
||||
display.getSize(displaySize);
|
||||
FileLog.e("tmessages", "display size = " + displaySize.x + " " + displaySize.y + " " + displayMetrics.xdpi + "x" + displayMetrics.ydpi);
|
||||
}
|
||||
}
|
||||
|
@ -685,7 +677,7 @@ public class AndroidUtilities {
|
|||
}
|
||||
|
||||
public static void clearCursorDrawable(EditText editText) {
|
||||
if (editText == null || Build.VERSION.SDK_INT < 12) {
|
||||
if (editText == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
@ -883,6 +875,19 @@ public class AndroidUtilities {
|
|||
return size;
|
||||
}
|
||||
|
||||
public static CharSequence getTrimmedString(CharSequence src) {
|
||||
if (src == null || src.length() == 0) {
|
||||
return src;
|
||||
}
|
||||
while (src.length() > 0 && (src.charAt(0) == '\n' || src.charAt(0) == ' ')) {
|
||||
src = src.subSequence(1, src.length());
|
||||
}
|
||||
while (src.length() > 0 && (src.charAt(src.length() - 1) == '\n' || src.charAt(src.length() - 1) == ' ')) {
|
||||
src = src.subSequence(0, src.length() - 1);
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
public static void setListViewEdgeEffectColor(AbsListView listView, int color) {
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
try {
|
||||
|
@ -989,14 +994,9 @@ public class AndroidUtilities {
|
|||
}
|
||||
|
||||
public static boolean needShowPasscode(boolean reset) {
|
||||
boolean wasInBackground;
|
||||
if (Build.VERSION.SDK_INT >= 14) {
|
||||
wasInBackground = ForegroundDetector.getInstance().isWasInBackground(reset);
|
||||
if (reset) {
|
||||
ForegroundDetector.getInstance().resetBackgroundVar();
|
||||
}
|
||||
} else {
|
||||
wasInBackground = UserConfig.lastPauseTime != 0;
|
||||
boolean wasInBackground = ForegroundDetector.getInstance().isWasInBackground(reset);
|
||||
if (reset) {
|
||||
ForegroundDetector.getInstance().resetBackgroundVar();
|
||||
}
|
||||
return UserConfig.passcodeHash.length() > 0 && wasInBackground &&
|
||||
(UserConfig.appLocked || UserConfig.autoLockIn != 0 && UserConfig.lastPauseTime != 0 && !UserConfig.appLocked && (UserConfig.lastPauseTime + UserConfig.autoLockIn) <= ConnectionsManager.getInstance().getCurrentTime());
|
||||
|
@ -1004,24 +1004,21 @@ public class AndroidUtilities {
|
|||
|
||||
public static void shakeView(final View view, final float x, final int num) {
|
||||
if (num == 6) {
|
||||
ViewProxy.setTranslationX(view, 0);
|
||||
view.clearAnimation();
|
||||
view.setTranslationX(0);
|
||||
return;
|
||||
}
|
||||
AnimatorSetProxy animatorSetProxy = new AnimatorSetProxy();
|
||||
animatorSetProxy.playTogether(ObjectAnimatorProxy.ofFloat(view, "translationX", AndroidUtilities.dp(x)));
|
||||
animatorSetProxy.setDuration(50);
|
||||
animatorSetProxy.addListener(new AnimatorListenerAdapterProxy() {
|
||||
AnimatorSet animatorSet = new AnimatorSet();
|
||||
animatorSet.playTogether(ObjectAnimator.ofFloat(view, "translationX", AndroidUtilities.dp(x)));
|
||||
animatorSet.setDuration(50);
|
||||
animatorSet.addListener(new AnimatorListenerAdapterProxy() {
|
||||
@Override
|
||||
public void onAnimationEnd(Object animation) {
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
shakeView(view, num == 5 ? 0 : -x, num + 1);
|
||||
}
|
||||
});
|
||||
animatorSetProxy.start();
|
||||
animatorSet.start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*public static String ellipsize(String text, int maxLines, int maxWidth, TextPaint paint) {
|
||||
if (text == null || paint == null) {
|
||||
return null;
|
||||
|
@ -1058,7 +1055,7 @@ public class AndroidUtilities {
|
|||
}*/
|
||||
|
||||
/*public static void turnOffHardwareAcceleration(Window window) {
|
||||
if (window == null || Build.MODEL == null || Build.VERSION.SDK_INT < 11) {
|
||||
if (window == null || Build.MODEL == null) {
|
||||
return;
|
||||
}
|
||||
if (Build.MODEL.contains("GT-S5301") ||
|
||||
|
@ -1094,14 +1091,9 @@ public class AndroidUtilities {
|
|||
|
||||
public static void addToClipboard(CharSequence str) {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT < 11) {
|
||||
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
clipboard.setText(str);
|
||||
} else {
|
||||
android.content.ClipboardManager clipboard = (android.content.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
android.content.ClipData clip = android.content.ClipData.newPlainText("label", str);
|
||||
clipboard.setPrimaryClip(clip);
|
||||
}
|
||||
android.content.ClipboardManager clipboard = (android.content.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
android.content.ClipData clip = android.content.ClipData.newPlainText("label", str);
|
||||
clipboard.setPrimaryClip(clip);
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
|
@ -1389,4 +1381,50 @@ public class AndroidUtilities {
|
|||
System.arraycopy(sha1, 0, key_hash, 0, 16);
|
||||
return key_hash;
|
||||
}
|
||||
|
||||
public static void openForView(MessageObject message, Activity activity) throws Exception {
|
||||
File f = null;
|
||||
String fileName = message.getFileName();
|
||||
if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) {
|
||||
f = new File(message.messageOwner.attachPath);
|
||||
}
|
||||
if (f == null || !f.exists()) {
|
||||
f = FileLoader.getPathToMessage(message.messageOwner);
|
||||
}
|
||||
if (f != null && f.exists()) {
|
||||
String realMimeType = null;
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
MimeTypeMap myMime = MimeTypeMap.getSingleton();
|
||||
int idx = fileName.lastIndexOf('.');
|
||||
if (idx != -1) {
|
||||
String ext = fileName.substring(idx + 1);
|
||||
realMimeType = myMime.getMimeTypeFromExtension(ext.toLowerCase());
|
||||
if (realMimeType == null) {
|
||||
if (message.type == 9 || message.type == 0) {
|
||||
realMimeType = message.getDocument().mime_type;
|
||||
}
|
||||
if (realMimeType == null || realMimeType.length() == 0) {
|
||||
realMimeType = null;
|
||||
}
|
||||
}
|
||||
if (realMimeType != null) {
|
||||
intent.setDataAndType(Uri.fromFile(f), realMimeType);
|
||||
} else {
|
||||
intent.setDataAndType(Uri.fromFile(f), "text/plain");
|
||||
}
|
||||
} else {
|
||||
intent.setDataAndType(Uri.fromFile(f), "text/plain");
|
||||
}
|
||||
if (realMimeType != null) {
|
||||
try {
|
||||
activity.startActivityForResult(intent, 500);
|
||||
} catch (Exception e) {
|
||||
intent.setDataAndType(Uri.fromFile(f), "text/plain");
|
||||
activity.startActivityForResult(intent, 500);
|
||||
}
|
||||
} else {
|
||||
activity.startActivityForResult(intent, 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,191 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.Animation;
|
||||
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public abstract class Animator10 implements Cloneable {
|
||||
|
||||
ArrayList<AnimatorListener> mListeners = null;
|
||||
ArrayList<AnimatorPauseListener> mPauseListeners = null;
|
||||
boolean mPaused = false;
|
||||
|
||||
public abstract long getStartDelay();
|
||||
|
||||
public abstract void setStartDelay(long startDelay);
|
||||
|
||||
public abstract Animator10 setDuration(long duration);
|
||||
|
||||
public abstract long getDuration();
|
||||
|
||||
public abstract void setInterpolator(Interpolator value);
|
||||
|
||||
public abstract boolean isRunning();
|
||||
|
||||
public void start() {
|
||||
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
|
||||
}
|
||||
|
||||
public void end() {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void pause() {
|
||||
if (isStarted() && !mPaused) {
|
||||
mPaused = true;
|
||||
if (mPauseListeners != null) {
|
||||
ArrayList<AnimatorPauseListener> tmpListeners = (ArrayList<AnimatorPauseListener>) mPauseListeners.clone();
|
||||
int numListeners = tmpListeners.size();
|
||||
for (AnimatorPauseListener tmpListener : tmpListeners) {
|
||||
tmpListener.onAnimationPause(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void resume() {
|
||||
if (mPaused) {
|
||||
mPaused = false;
|
||||
if (mPauseListeners != null) {
|
||||
ArrayList<AnimatorPauseListener> tmpListeners = (ArrayList<AnimatorPauseListener>) mPauseListeners.clone();
|
||||
int numListeners = tmpListeners.size();
|
||||
for (AnimatorPauseListener tmpListener : tmpListeners) {
|
||||
tmpListener.onAnimationResume(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPaused() {
|
||||
return mPaused;
|
||||
}
|
||||
|
||||
public boolean isStarted() {
|
||||
return isRunning();
|
||||
}
|
||||
|
||||
public Interpolator getInterpolator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addListener(AnimatorListener listener) {
|
||||
if (mListeners == null) {
|
||||
mListeners = new ArrayList<AnimatorListener>();
|
||||
}
|
||||
mListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(AnimatorListener listener) {
|
||||
if (mListeners == null) {
|
||||
return;
|
||||
}
|
||||
mListeners.remove(listener);
|
||||
if (mListeners.size() == 0) {
|
||||
mListeners = null;
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayList<AnimatorListener> getListeners() {
|
||||
return mListeners;
|
||||
}
|
||||
|
||||
public void addPauseListener(AnimatorPauseListener listener) {
|
||||
if (mPauseListeners == null) {
|
||||
mPauseListeners = new ArrayList<AnimatorPauseListener>();
|
||||
}
|
||||
mPauseListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removePauseListener(AnimatorPauseListener listener) {
|
||||
if (mPauseListeners == null) {
|
||||
return;
|
||||
}
|
||||
mPauseListeners.remove(listener);
|
||||
if (mPauseListeners.size() == 0) {
|
||||
mPauseListeners = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAllListeners() {
|
||||
if (mListeners != null) {
|
||||
mListeners.clear();
|
||||
mListeners = null;
|
||||
}
|
||||
if (mPauseListeners != null) {
|
||||
mPauseListeners.clear();
|
||||
mPauseListeners = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Animator10 clone() {
|
||||
try {
|
||||
final Animator10 anim = (Animator10) super.clone();
|
||||
if (mListeners != null) {
|
||||
ArrayList<AnimatorListener> oldListeners = mListeners;
|
||||
anim.mListeners = new ArrayList<AnimatorListener>();
|
||||
int numListeners = oldListeners.size();
|
||||
for (AnimatorListener oldListener : oldListeners) {
|
||||
anim.mListeners.add(oldListener);
|
||||
}
|
||||
}
|
||||
if (mPauseListeners != null) {
|
||||
ArrayList<AnimatorPauseListener> oldListeners = mPauseListeners;
|
||||
anim.mPauseListeners = new ArrayList<AnimatorPauseListener>();
|
||||
int numListeners = oldListeners.size();
|
||||
for (AnimatorPauseListener oldListener : oldListeners) {
|
||||
anim.mPauseListeners.add(oldListener);
|
||||
}
|
||||
}
|
||||
return anim;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
public void setupStartValues() {
|
||||
|
||||
}
|
||||
|
||||
public void setupEndValues() {
|
||||
|
||||
}
|
||||
|
||||
public void setTarget(Object target) {
|
||||
|
||||
}
|
||||
|
||||
public interface AnimatorListener {
|
||||
void onAnimationStart(Animator10 animation);
|
||||
void onAnimationEnd(Animator10 animation);
|
||||
void onAnimationCancel(Animator10 animation);
|
||||
void onAnimationRepeat(Animator10 animation);
|
||||
}
|
||||
|
||||
public interface AnimatorPauseListener {
|
||||
void onAnimationPause(Animator10 animation);
|
||||
void onAnimationResume(Animator10 animation);
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.Animation;
|
||||
|
||||
public abstract class AnimatorListenerAdapter10 implements Animator10.AnimatorListener, Animator10.AnimatorPauseListener {
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator10 animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator10 animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator10 animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator10 animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationPause(Animator10 animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationResume(Animator10 animation) {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,705 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.Animation;
|
||||
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public final class AnimatorSet10 extends Animator10 {
|
||||
|
||||
private ArrayList<Animator10> mPlayingSet = new ArrayList<>();
|
||||
private HashMap<Animator10, Node> mNodeMap = new HashMap<>();
|
||||
private ArrayList<Node> mNodes = new ArrayList<>();
|
||||
private ArrayList<Node> mSortedNodes = new ArrayList<>();
|
||||
private boolean mNeedsSort = true;
|
||||
private AnimatorSetListener mSetListener = null;
|
||||
boolean mTerminated = false;
|
||||
private boolean mStarted = false;
|
||||
private long mStartDelay = 0;
|
||||
private ValueAnimator mDelayAnim = null;
|
||||
private long mDuration = -1;
|
||||
private Interpolator mInterpolator = null;
|
||||
|
||||
public void playTogether(Animator10... items) {
|
||||
if (items != null) {
|
||||
mNeedsSort = true;
|
||||
Builder builder = play(items[0]);
|
||||
for (int i = 1; i < items.length; ++i) {
|
||||
builder.with(items[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void playTogether(Collection<Animator10> items) {
|
||||
if (items != null && items.size() > 0) {
|
||||
mNeedsSort = true;
|
||||
Builder builder = null;
|
||||
for (Animator10 anim : items) {
|
||||
if (builder == null) {
|
||||
builder = play(anim);
|
||||
} else {
|
||||
builder.with(anim);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void playSequentially(Animator10... items) {
|
||||
if (items != null) {
|
||||
mNeedsSort = true;
|
||||
if (items.length == 1) {
|
||||
play(items[0]);
|
||||
} else {
|
||||
for (int i = 0; i < items.length - 1; ++i) {
|
||||
play(items[i]).before(items[i+1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void playSequentially(List<Animator10> items) {
|
||||
if (items != null && items.size() > 0) {
|
||||
mNeedsSort = true;
|
||||
if (items.size() == 1) {
|
||||
play(items.get(0));
|
||||
} else {
|
||||
for (int i = 0; i < items.size() - 1; ++i) {
|
||||
play(items.get(i)).before(items.get(i+1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayList<Animator10> getChildAnimations() {
|
||||
ArrayList<Animator10> childList = new ArrayList<>();
|
||||
for (Node node : mNodes) {
|
||||
childList.add(node.animation);
|
||||
}
|
||||
return childList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTarget(Object target) {
|
||||
for (Node node : mNodes) {
|
||||
Animator10 animation = node.animation;
|
||||
if (animation instanceof AnimatorSet10) {
|
||||
animation.setTarget(target);
|
||||
} else if (animation instanceof ObjectAnimator10) {
|
||||
animation.setTarget(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInterpolator(Interpolator interpolator) {
|
||||
mInterpolator = interpolator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Interpolator getInterpolator() {
|
||||
return mInterpolator;
|
||||
}
|
||||
|
||||
public Builder play(Animator10 anim) {
|
||||
if (anim != null) {
|
||||
mNeedsSort = true;
|
||||
return new Builder(anim);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void cancel() {
|
||||
mTerminated = true;
|
||||
if (isStarted()) {
|
||||
ArrayList<AnimatorListener> tmpListeners = null;
|
||||
if (mListeners != null) {
|
||||
tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
|
||||
for (AnimatorListener listener : tmpListeners) {
|
||||
listener.onAnimationCancel(this);
|
||||
}
|
||||
}
|
||||
if (mDelayAnim != null && mDelayAnim.isRunning()) {
|
||||
mDelayAnim.cancel();
|
||||
} else if (mSortedNodes.size() > 0) {
|
||||
for (Node node : mSortedNodes) {
|
||||
node.animation.cancel();
|
||||
}
|
||||
}
|
||||
if (tmpListeners != null) {
|
||||
for (AnimatorListener listener : tmpListeners) {
|
||||
listener.onAnimationEnd(this);
|
||||
}
|
||||
}
|
||||
mStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void end() {
|
||||
mTerminated = true;
|
||||
if (isStarted()) {
|
||||
if (mSortedNodes.size() != mNodes.size()) {
|
||||
// hasn't been started yet - sort the nodes now, then end them
|
||||
sortNodes();
|
||||
for (Node node : mSortedNodes) {
|
||||
if (mSetListener == null) {
|
||||
mSetListener = new AnimatorSetListener(this);
|
||||
}
|
||||
node.animation.addListener(mSetListener);
|
||||
}
|
||||
}
|
||||
if (mDelayAnim != null) {
|
||||
mDelayAnim.cancel();
|
||||
}
|
||||
if (mSortedNodes.size() > 0) {
|
||||
for (Node node : mSortedNodes) {
|
||||
node.animation.end();
|
||||
}
|
||||
}
|
||||
if (mListeners != null) {
|
||||
ArrayList<AnimatorListener> tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
|
||||
for (AnimatorListener listener : tmpListeners) {
|
||||
listener.onAnimationEnd(this);
|
||||
}
|
||||
}
|
||||
mStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
for (Node node : mNodes) {
|
||||
if (node.animation.isRunning()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStarted() {
|
||||
return mStarted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartDelay() {
|
||||
return mStartDelay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStartDelay(long startDelay) {
|
||||
mStartDelay = startDelay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
return mDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimatorSet10 setDuration(long duration) {
|
||||
if (duration < 0) {
|
||||
throw new IllegalArgumentException("duration must be a value of zero or greater");
|
||||
}
|
||||
mDuration = duration;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupStartValues() {
|
||||
for (Node node : mNodes) {
|
||||
node.animation.setupStartValues();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupEndValues() {
|
||||
for (Node node : mNodes) {
|
||||
node.animation.setupEndValues();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause() {
|
||||
boolean previouslyPaused = mPaused;
|
||||
super.pause();
|
||||
if (!previouslyPaused && mPaused) {
|
||||
if (mDelayAnim != null) {
|
||||
mDelayAnim.pause();
|
||||
} else {
|
||||
for (Node node : mNodes) {
|
||||
node.animation.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume() {
|
||||
boolean previouslyPaused = mPaused;
|
||||
super.resume();
|
||||
if (previouslyPaused && !mPaused) {
|
||||
if (mDelayAnim != null) {
|
||||
mDelayAnim.resume();
|
||||
} else {
|
||||
for (Node node : mNodes) {
|
||||
node.animation.resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void start() {
|
||||
mTerminated = false;
|
||||
mStarted = true;
|
||||
mPaused = false;
|
||||
|
||||
if (mDuration >= 0) {
|
||||
for (Node node : mNodes) {
|
||||
node.animation.setDuration(mDuration);
|
||||
}
|
||||
}
|
||||
if (mInterpolator != null) {
|
||||
for (Node node : mNodes) {
|
||||
node.animation.setInterpolator(mInterpolator);
|
||||
}
|
||||
}
|
||||
|
||||
sortNodes();
|
||||
|
||||
int numSortedNodes = mSortedNodes.size();
|
||||
for (Node node : mSortedNodes) {
|
||||
ArrayList<AnimatorListener> oldListeners = node.animation.getListeners();
|
||||
if (oldListeners != null && oldListeners.size() > 0) {
|
||||
final ArrayList<AnimatorListener> clonedListeners = new
|
||||
ArrayList<>(oldListeners);
|
||||
|
||||
for (AnimatorListener listener : clonedListeners) {
|
||||
if (listener instanceof DependencyListener ||
|
||||
listener instanceof AnimatorSetListener) {
|
||||
node.animation.removeListener(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final ArrayList<Node> nodesToStart = new ArrayList<>();
|
||||
for (Node node : mSortedNodes) {
|
||||
if (mSetListener == null) {
|
||||
mSetListener = new AnimatorSetListener(this);
|
||||
}
|
||||
if (node.dependencies == null || node.dependencies.size() == 0) {
|
||||
nodesToStart.add(node);
|
||||
} else {
|
||||
int numDependencies = node.dependencies.size();
|
||||
for (int j = 0; j < numDependencies; ++j) {
|
||||
Dependency dependency = node.dependencies.get(j);
|
||||
dependency.node.animation.addListener(
|
||||
new DependencyListener(this, node, dependency.rule));
|
||||
}
|
||||
node.tmpDependencies = (ArrayList<Dependency>) node.dependencies.clone();
|
||||
}
|
||||
node.animation.addListener(mSetListener);
|
||||
}
|
||||
|
||||
if (mStartDelay <= 0) {
|
||||
for (Node node : nodesToStart) {
|
||||
node.animation.start();
|
||||
mPlayingSet.add(node.animation);
|
||||
}
|
||||
} else {
|
||||
mDelayAnim = ValueAnimator.ofFloat(0f, 1f);
|
||||
mDelayAnim.setDuration(mStartDelay);
|
||||
mDelayAnim.addListener(new AnimatorListenerAdapter10() {
|
||||
boolean canceled = false;
|
||||
public void onAnimationCancel(Animator10 anim) {
|
||||
canceled = true;
|
||||
}
|
||||
public void onAnimationEnd(Animator10 anim) {
|
||||
if (!canceled) {
|
||||
int numNodes = nodesToStart.size();
|
||||
for (Node node : nodesToStart) {
|
||||
node.animation.start();
|
||||
mPlayingSet.add(node.animation);
|
||||
}
|
||||
}
|
||||
mDelayAnim = null;
|
||||
}
|
||||
});
|
||||
mDelayAnim.start();
|
||||
}
|
||||
if (mListeners != null) {
|
||||
ArrayList<AnimatorListener> tmpListeners =
|
||||
(ArrayList<AnimatorListener>) mListeners.clone();
|
||||
int numListeners = tmpListeners.size();
|
||||
for (AnimatorListener tmpListener : tmpListeners) {
|
||||
tmpListener.onAnimationStart(this);
|
||||
}
|
||||
}
|
||||
if (mNodes.size() == 0 && mStartDelay == 0) {
|
||||
mStarted = false;
|
||||
if (mListeners != null) {
|
||||
ArrayList<AnimatorListener> tmpListeners =
|
||||
(ArrayList<AnimatorListener>) mListeners.clone();
|
||||
int numListeners = tmpListeners.size();
|
||||
for (AnimatorListener tmpListener : tmpListeners) {
|
||||
tmpListener.onAnimationEnd(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimatorSet10 clone() {
|
||||
final AnimatorSet10 anim = (AnimatorSet10) super.clone();
|
||||
|
||||
anim.mNeedsSort = true;
|
||||
anim.mTerminated = false;
|
||||
anim.mStarted = false;
|
||||
anim.mPlayingSet = new ArrayList<>();
|
||||
anim.mNodeMap = new HashMap<>();
|
||||
anim.mNodes = new ArrayList<>();
|
||||
anim.mSortedNodes = new ArrayList<>();
|
||||
|
||||
HashMap<Node, Node> nodeCloneMap = new HashMap<>();
|
||||
for (Node node : mNodes) {
|
||||
Node nodeClone = node.clone();
|
||||
nodeCloneMap.put(node, nodeClone);
|
||||
anim.mNodes.add(nodeClone);
|
||||
anim.mNodeMap.put(nodeClone.animation, nodeClone);
|
||||
nodeClone.dependencies = null;
|
||||
nodeClone.tmpDependencies = null;
|
||||
nodeClone.nodeDependents = null;
|
||||
nodeClone.nodeDependencies = null;
|
||||
ArrayList<AnimatorListener> cloneListeners = nodeClone.animation.getListeners();
|
||||
if (cloneListeners != null) {
|
||||
ArrayList<AnimatorListener> listenersToRemove = null;
|
||||
for (AnimatorListener listener : cloneListeners) {
|
||||
if (listener instanceof AnimatorSetListener) {
|
||||
if (listenersToRemove == null) {
|
||||
listenersToRemove = new ArrayList<>();
|
||||
}
|
||||
listenersToRemove.add(listener);
|
||||
}
|
||||
}
|
||||
if (listenersToRemove != null) {
|
||||
for (AnimatorListener listener : listenersToRemove) {
|
||||
cloneListeners.remove(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Node node : mNodes) {
|
||||
Node nodeClone = nodeCloneMap.get(node);
|
||||
if (node.dependencies != null) {
|
||||
for (Dependency dependency : node.dependencies) {
|
||||
Node clonedDependencyNode = nodeCloneMap.get(dependency.node);
|
||||
Dependency cloneDependency = new Dependency(clonedDependencyNode, dependency.rule);
|
||||
nodeClone.addDependency(cloneDependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
return anim;
|
||||
}
|
||||
|
||||
private static class DependencyListener implements AnimatorListener {
|
||||
|
||||
private AnimatorSet10 mAnimatorSet;
|
||||
private Node mNode;
|
||||
private int mRule;
|
||||
|
||||
public DependencyListener(AnimatorSet10 animatorSet, Node node, int rule) {
|
||||
this.mAnimatorSet = animatorSet;
|
||||
this.mNode = node;
|
||||
this.mRule = rule;
|
||||
}
|
||||
|
||||
public void onAnimationCancel(Animator10 animation) {
|
||||
|
||||
}
|
||||
|
||||
public void onAnimationEnd(Animator10 animation) {
|
||||
if (mRule == Dependency.AFTER) {
|
||||
startIfReady(animation);
|
||||
}
|
||||
}
|
||||
|
||||
public void onAnimationRepeat(Animator10 animation) {
|
||||
|
||||
}
|
||||
|
||||
public void onAnimationStart(Animator10 animation) {
|
||||
if (mRule == Dependency.WITH) {
|
||||
startIfReady(animation);
|
||||
}
|
||||
}
|
||||
|
||||
private void startIfReady(Animator10 dependencyAnimation) {
|
||||
if (mAnimatorSet.mTerminated) {
|
||||
return;
|
||||
}
|
||||
Dependency dependencyToRemove = null;
|
||||
int numDependencies = mNode.tmpDependencies.size();
|
||||
for (int i = 0; i < numDependencies; ++i) {
|
||||
Dependency dependency = mNode.tmpDependencies.get(i);
|
||||
if (dependency.rule == mRule && dependency.node.animation == dependencyAnimation) {
|
||||
dependencyToRemove = dependency;
|
||||
dependencyAnimation.removeListener(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mNode.tmpDependencies.remove(dependencyToRemove);
|
||||
if (mNode.tmpDependencies.size() == 0) {
|
||||
mNode.animation.start();
|
||||
mAnimatorSet.mPlayingSet.add(mNode.animation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class AnimatorSetListener implements AnimatorListener {
|
||||
|
||||
private AnimatorSet10 mAnimatorSet;
|
||||
|
||||
AnimatorSetListener(AnimatorSet10 animatorSet) {
|
||||
mAnimatorSet = animatorSet;
|
||||
}
|
||||
|
||||
public void onAnimationCancel(Animator10 animation) {
|
||||
if (!mTerminated) {
|
||||
if (mPlayingSet.size() == 0) {
|
||||
if (mListeners != null) {
|
||||
int numListeners = mListeners.size();
|
||||
for (AnimatorListener mListener : mListeners) {
|
||||
mListener.onAnimationCancel(mAnimatorSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onAnimationEnd(Animator10 animation) {
|
||||
animation.removeListener(this);
|
||||
mPlayingSet.remove(animation);
|
||||
Node animNode = mAnimatorSet.mNodeMap.get(animation);
|
||||
animNode.done = true;
|
||||
if (!mTerminated) {
|
||||
ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes;
|
||||
boolean allDone = true;
|
||||
int numSortedNodes = sortedNodes.size();
|
||||
for (Node sortedNode : sortedNodes) {
|
||||
if (!sortedNode.done) {
|
||||
allDone = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allDone) {
|
||||
if (mListeners != null) {
|
||||
ArrayList<AnimatorListener> tmpListeners =
|
||||
(ArrayList<AnimatorListener>) mListeners.clone();
|
||||
int numListeners = tmpListeners.size();
|
||||
for (AnimatorListener tmpListener : tmpListeners) {
|
||||
tmpListener.onAnimationEnd(mAnimatorSet);
|
||||
}
|
||||
}
|
||||
mAnimatorSet.mStarted = false;
|
||||
mAnimatorSet.mPaused = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onAnimationRepeat(Animator10 animation) {
|
||||
|
||||
}
|
||||
|
||||
public void onAnimationStart(Animator10 animation) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void sortNodes() {
|
||||
if (mNeedsSort) {
|
||||
mSortedNodes.clear();
|
||||
ArrayList<Node> roots = new ArrayList<>();
|
||||
int numNodes = mNodes.size();
|
||||
for (Node node : mNodes) {
|
||||
if (node.dependencies == null || node.dependencies.size() == 0) {
|
||||
roots.add(node);
|
||||
}
|
||||
}
|
||||
ArrayList<Node> tmpRoots = new ArrayList<>();
|
||||
while (roots.size() > 0) {
|
||||
int numRoots = roots.size();
|
||||
for (Node root : roots) {
|
||||
mSortedNodes.add(root);
|
||||
if (root.nodeDependents != null) {
|
||||
int numDependents = root.nodeDependents.size();
|
||||
for (int j = 0; j < numDependents; ++j) {
|
||||
Node node = root.nodeDependents.get(j);
|
||||
node.nodeDependencies.remove(root);
|
||||
if (node.nodeDependencies.size() == 0) {
|
||||
tmpRoots.add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
roots.clear();
|
||||
roots.addAll(tmpRoots);
|
||||
tmpRoots.clear();
|
||||
}
|
||||
mNeedsSort = false;
|
||||
if (mSortedNodes.size() != mNodes.size()) {
|
||||
throw new IllegalStateException("Circular dependencies cannot exist in AnimatorSet");
|
||||
}
|
||||
} else {
|
||||
int numNodes = mNodes.size();
|
||||
for (Node node : mNodes) {
|
||||
if (node.dependencies != null && node.dependencies.size() > 0) {
|
||||
int numDependencies = node.dependencies.size();
|
||||
for (int j = 0; j < numDependencies; ++j) {
|
||||
Dependency dependency = node.dependencies.get(j);
|
||||
if (node.nodeDependencies == null) {
|
||||
node.nodeDependencies = new ArrayList<>();
|
||||
}
|
||||
if (!node.nodeDependencies.contains(dependency.node)) {
|
||||
node.nodeDependencies.add(dependency.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
node.done = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Dependency {
|
||||
static final int WITH = 0;
|
||||
static final int AFTER = 1;
|
||||
public Node node;
|
||||
public int rule;
|
||||
|
||||
public Dependency(Node node, int rule) {
|
||||
this.node = node;
|
||||
this.rule = rule;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Node implements Cloneable {
|
||||
public Animator10 animation;
|
||||
public ArrayList<Dependency> dependencies = null;
|
||||
public ArrayList<Dependency> tmpDependencies = null;
|
||||
public ArrayList<Node> nodeDependencies = null;
|
||||
public ArrayList<Node> nodeDependents = null;
|
||||
public boolean done = false;
|
||||
|
||||
public Node(Animator10 animation) {
|
||||
this.animation = animation;
|
||||
}
|
||||
|
||||
public void addDependency(Dependency dependency) {
|
||||
if (dependencies == null) {
|
||||
dependencies = new ArrayList<>();
|
||||
nodeDependencies = new ArrayList<>();
|
||||
}
|
||||
dependencies.add(dependency);
|
||||
if (!nodeDependencies.contains(dependency.node)) {
|
||||
nodeDependencies.add(dependency.node);
|
||||
}
|
||||
Node dependencyNode = dependency.node;
|
||||
if (dependencyNode.nodeDependents == null) {
|
||||
dependencyNode.nodeDependents = new ArrayList<>();
|
||||
}
|
||||
dependencyNode.nodeDependents.add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node clone() {
|
||||
try {
|
||||
Node node = (Node) super.clone();
|
||||
node.animation = animation.clone();
|
||||
return node;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Builder {
|
||||
|
||||
private Node mCurrentNode;
|
||||
|
||||
Builder(Animator10 anim) {
|
||||
mCurrentNode = mNodeMap.get(anim);
|
||||
if (mCurrentNode == null) {
|
||||
mCurrentNode = new Node(anim);
|
||||
mNodeMap.put(anim, mCurrentNode);
|
||||
mNodes.add(mCurrentNode);
|
||||
}
|
||||
}
|
||||
|
||||
public Builder with(Animator10 anim) {
|
||||
Node node = mNodeMap.get(anim);
|
||||
if (node == null) {
|
||||
node = new Node(anim);
|
||||
mNodeMap.put(anim, node);
|
||||
mNodes.add(node);
|
||||
}
|
||||
Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH);
|
||||
node.addDependency(dependency);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder before(Animator10 anim) {
|
||||
Node node = mNodeMap.get(anim);
|
||||
if (node == null) {
|
||||
node = new Node(anim);
|
||||
mNodeMap.put(anim, node);
|
||||
mNodes.add(node);
|
||||
}
|
||||
Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER);
|
||||
node.addDependency(dependency);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder after(Animator10 anim) {
|
||||
Node node = mNodeMap.get(anim);
|
||||
if (node == null) {
|
||||
node = new Node(anim);
|
||||
mNodeMap.put(anim, node);
|
||||
mNodes.add(node);
|
||||
}
|
||||
Dependency dependency = new Dependency(node, Dependency.AFTER);
|
||||
mCurrentNode.addDependency(dependency);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder after(long delay) {
|
||||
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
|
||||
anim.setDuration(delay);
|
||||
after(anim);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.Animation;
|
||||
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import org.telegram.messenger.Animation.Keyframe.FloatKeyframe;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
class FloatKeyframeSet extends KeyframeSet {
|
||||
private float firstValue;
|
||||
private float lastValue;
|
||||
private float deltaValue;
|
||||
private boolean firstTime = true;
|
||||
|
||||
public FloatKeyframeSet(FloatKeyframe... keyframes) {
|
||||
super(keyframes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(float fraction) {
|
||||
return getFloatValue(fraction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatKeyframeSet clone() {
|
||||
ArrayList<Keyframe> keyframes = mKeyframes;
|
||||
int numKeyframes = mKeyframes.size();
|
||||
FloatKeyframe[] newKeyframes = new FloatKeyframe[numKeyframes];
|
||||
for (int i = 0; i < numKeyframes; ++i) {
|
||||
newKeyframes[i] = (FloatKeyframe) keyframes.get(i).clone();
|
||||
}
|
||||
return new FloatKeyframeSet(newKeyframes);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public float getFloatValue(float fraction) {
|
||||
if (mNumKeyframes == 2) {
|
||||
if (firstTime) {
|
||||
firstTime = false;
|
||||
firstValue = ((FloatKeyframe) mKeyframes.get(0)).getFloatValue();
|
||||
lastValue = ((FloatKeyframe) mKeyframes.get(1)).getFloatValue();
|
||||
deltaValue = lastValue - firstValue;
|
||||
}
|
||||
if (mInterpolator != null) {
|
||||
fraction = mInterpolator.getInterpolation(fraction);
|
||||
}
|
||||
if (mEvaluator == null) {
|
||||
return firstValue + fraction * deltaValue;
|
||||
} else {
|
||||
return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).floatValue();
|
||||
}
|
||||
}
|
||||
if (fraction <= 0f) {
|
||||
final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
|
||||
final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(1);
|
||||
float prevValue = prevKeyframe.getFloatValue();
|
||||
float nextValue = nextKeyframe.getFloatValue();
|
||||
float prevFraction = prevKeyframe.getFraction();
|
||||
float nextFraction = nextKeyframe.getFraction();
|
||||
final Interpolator interpolator = nextKeyframe.getInterpolator();
|
||||
if (interpolator != null) {
|
||||
fraction = interpolator.getInterpolation(fraction);
|
||||
}
|
||||
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
|
||||
return mEvaluator == null ? prevValue + intervalFraction * (nextValue - prevValue) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).floatValue();
|
||||
} else if (fraction >= 1f) {
|
||||
final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 2);
|
||||
final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 1);
|
||||
float prevValue = prevKeyframe.getFloatValue();
|
||||
float nextValue = nextKeyframe.getFloatValue();
|
||||
float prevFraction = prevKeyframe.getFraction();
|
||||
float nextFraction = nextKeyframe.getFraction();
|
||||
final Interpolator interpolator = nextKeyframe.getInterpolator();
|
||||
if (interpolator != null) {
|
||||
fraction = interpolator.getInterpolation(fraction);
|
||||
}
|
||||
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
|
||||
return mEvaluator == null ? prevValue + intervalFraction * (nextValue - prevValue) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).floatValue();
|
||||
}
|
||||
FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
|
||||
for (int i = 1; i < mNumKeyframes; ++i) {
|
||||
FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i);
|
||||
if (fraction < nextKeyframe.getFraction()) {
|
||||
final Interpolator interpolator = nextKeyframe.getInterpolator();
|
||||
if (interpolator != null) {
|
||||
fraction = interpolator.getInterpolation(fraction);
|
||||
}
|
||||
float intervalFraction = (fraction - prevKeyframe.getFraction()) /
|
||||
(nextKeyframe.getFraction() - prevKeyframe.getFraction());
|
||||
float prevValue = prevKeyframe.getFloatValue();
|
||||
float nextValue = nextKeyframe.getFloatValue();
|
||||
return mEvaluator == null ? prevValue + intervalFraction * (nextValue - prevValue) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).floatValue();
|
||||
}
|
||||
prevKeyframe = nextKeyframe;
|
||||
}
|
||||
return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue();
|
||||
}
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.Animation;
|
||||
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import org.telegram.messenger.Animation.Keyframe.IntKeyframe;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
class IntKeyframeSet extends KeyframeSet {
|
||||
private int firstValue;
|
||||
private int lastValue;
|
||||
private int deltaValue;
|
||||
private boolean firstTime = true;
|
||||
|
||||
public IntKeyframeSet(IntKeyframe... keyframes) {
|
||||
super(keyframes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(float fraction) {
|
||||
return getIntValue(fraction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntKeyframeSet clone() {
|
||||
ArrayList<Keyframe> keyframes = mKeyframes;
|
||||
int numKeyframes = mKeyframes.size();
|
||||
IntKeyframe[] newKeyframes = new IntKeyframe[numKeyframes];
|
||||
for (int i = 0; i < numKeyframes; ++i) {
|
||||
newKeyframes[i] = (IntKeyframe) keyframes.get(i).clone();
|
||||
}
|
||||
return new IntKeyframeSet(newKeyframes);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public int getIntValue(float fraction) {
|
||||
if (mNumKeyframes == 2) {
|
||||
if (firstTime) {
|
||||
firstTime = false;
|
||||
firstValue = ((IntKeyframe) mKeyframes.get(0)).getIntValue();
|
||||
lastValue = ((IntKeyframe) mKeyframes.get(1)).getIntValue();
|
||||
deltaValue = lastValue - firstValue;
|
||||
}
|
||||
if (mInterpolator != null) {
|
||||
fraction = mInterpolator.getInterpolation(fraction);
|
||||
}
|
||||
if (mEvaluator == null) {
|
||||
return firstValue + (int)(fraction * deltaValue);
|
||||
} else {
|
||||
return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).intValue();
|
||||
}
|
||||
}
|
||||
if (fraction <= 0f) {
|
||||
final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0);
|
||||
final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(1);
|
||||
int prevValue = prevKeyframe.getIntValue();
|
||||
int nextValue = nextKeyframe.getIntValue();
|
||||
float prevFraction = prevKeyframe.getFraction();
|
||||
float nextFraction = nextKeyframe.getFraction();
|
||||
final Interpolator interpolator = nextKeyframe.getInterpolator();
|
||||
if (interpolator != null) {
|
||||
fraction = interpolator.getInterpolation(fraction);
|
||||
}
|
||||
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
|
||||
return mEvaluator == null ? prevValue + (int)(intervalFraction * (nextValue - prevValue)) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).intValue();
|
||||
} else if (fraction >= 1f) {
|
||||
final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 2);
|
||||
final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 1);
|
||||
int prevValue = prevKeyframe.getIntValue();
|
||||
int nextValue = nextKeyframe.getIntValue();
|
||||
float prevFraction = prevKeyframe.getFraction();
|
||||
float nextFraction = nextKeyframe.getFraction();
|
||||
final Interpolator interpolator = nextKeyframe.getInterpolator();
|
||||
if (interpolator != null) {
|
||||
fraction = interpolator.getInterpolation(fraction);
|
||||
}
|
||||
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
|
||||
return mEvaluator == null ? prevValue + (int)(intervalFraction * (nextValue - prevValue)) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).intValue();
|
||||
}
|
||||
IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0);
|
||||
for (int i = 1; i < mNumKeyframes; ++i) {
|
||||
IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(i);
|
||||
if (fraction < nextKeyframe.getFraction()) {
|
||||
final Interpolator interpolator = nextKeyframe.getInterpolator();
|
||||
if (interpolator != null) {
|
||||
fraction = interpolator.getInterpolation(fraction);
|
||||
}
|
||||
float intervalFraction = (fraction - prevKeyframe.getFraction()) / (nextKeyframe.getFraction() - prevKeyframe.getFraction());
|
||||
int prevValue = prevKeyframe.getIntValue();
|
||||
int nextValue = nextKeyframe.getIntValue();
|
||||
return mEvaluator == null ? prevValue + (int)(intervalFraction * (nextValue - prevValue)) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).intValue();
|
||||
}
|
||||
prevKeyframe = nextKeyframe;
|
||||
}
|
||||
return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).intValue();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.Animation;
|
||||
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
public abstract class Keyframe implements Cloneable {
|
||||
|
||||
float mFraction;
|
||||
Class mValueType;
|
||||
private Interpolator mInterpolator = null;
|
||||
boolean mHasValue = false;
|
||||
|
||||
public static Keyframe ofInt(float fraction, int value) {
|
||||
return new IntKeyframe(fraction, value);
|
||||
}
|
||||
|
||||
public static Keyframe ofInt(float fraction) {
|
||||
return new IntKeyframe(fraction);
|
||||
}
|
||||
|
||||
public static Keyframe ofFloat(float fraction, float value) {
|
||||
return new FloatKeyframe(fraction, value);
|
||||
}
|
||||
|
||||
public static Keyframe ofFloat(float fraction) {
|
||||
return new FloatKeyframe(fraction);
|
||||
}
|
||||
|
||||
public static Keyframe ofObject(float fraction, Object value) {
|
||||
return new ObjectKeyframe(fraction, value);
|
||||
}
|
||||
|
||||
public static Keyframe ofObject(float fraction) {
|
||||
return new ObjectKeyframe(fraction, null);
|
||||
}
|
||||
|
||||
public boolean hasValue() {
|
||||
return mHasValue;
|
||||
}
|
||||
|
||||
public abstract Object getValue();
|
||||
public abstract void setValue(Object value);
|
||||
|
||||
public float getFraction() {
|
||||
return mFraction;
|
||||
}
|
||||
|
||||
public void setFraction(float fraction) {
|
||||
mFraction = fraction;
|
||||
}
|
||||
|
||||
public Interpolator getInterpolator() {
|
||||
return mInterpolator;
|
||||
}
|
||||
|
||||
public void setInterpolator(Interpolator interpolator) {
|
||||
mInterpolator = interpolator;
|
||||
}
|
||||
|
||||
public Class getType() {
|
||||
return mValueType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract Keyframe clone();
|
||||
|
||||
static class ObjectKeyframe extends Keyframe {
|
||||
|
||||
Object mValue;
|
||||
|
||||
ObjectKeyframe(float fraction, Object value) {
|
||||
mFraction = fraction;
|
||||
mValue = value;
|
||||
mHasValue = (value != null);
|
||||
mValueType = mHasValue ? value.getClass() : Object.class;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public void setValue(Object value) {
|
||||
mValue = value;
|
||||
mHasValue = (value != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectKeyframe clone() {
|
||||
ObjectKeyframe kfClone = new ObjectKeyframe(getFraction(), mHasValue ? mValue : null);
|
||||
kfClone.setInterpolator(getInterpolator());
|
||||
return kfClone;
|
||||
}
|
||||
}
|
||||
|
||||
static class IntKeyframe extends Keyframe {
|
||||
|
||||
int mValue;
|
||||
|
||||
IntKeyframe(float fraction, int value) {
|
||||
mFraction = fraction;
|
||||
mValue = value;
|
||||
mValueType = int.class;
|
||||
mHasValue = true;
|
||||
}
|
||||
|
||||
IntKeyframe(float fraction) {
|
||||
mFraction = fraction;
|
||||
mValueType = int.class;
|
||||
}
|
||||
|
||||
public int getIntValue() {
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public void setValue(Object value) {
|
||||
if (value != null && value.getClass() == Integer.class) {
|
||||
mValue = (Integer) value;
|
||||
mHasValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntKeyframe clone() {
|
||||
IntKeyframe kfClone = mHasValue ? new IntKeyframe(getFraction(), mValue) : new IntKeyframe(getFraction());
|
||||
kfClone.setInterpolator(getInterpolator());
|
||||
return kfClone;
|
||||
}
|
||||
}
|
||||
|
||||
static class FloatKeyframe extends Keyframe {
|
||||
|
||||
float mValue;
|
||||
|
||||
FloatKeyframe(float fraction, float value) {
|
||||
mFraction = fraction;
|
||||
mValue = value;
|
||||
mValueType = float.class;
|
||||
mHasValue = true;
|
||||
}
|
||||
|
||||
FloatKeyframe(float fraction) {
|
||||
mFraction = fraction;
|
||||
mValueType = float.class;
|
||||
}
|
||||
|
||||
public float getFloatValue() {
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public void setValue(Object value) {
|
||||
if (value != null && value.getClass() == Float.class) {
|
||||
mValue = (Float) value;
|
||||
mHasValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatKeyframe clone() {
|
||||
FloatKeyframe kfClone = mHasValue ? new FloatKeyframe(getFraction(), mValue) : new FloatKeyframe(getFraction());
|
||||
kfClone.setInterpolator(getInterpolator());
|
||||
return kfClone;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,200 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.Animation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import android.util.Log;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import org.telegram.messenger.Animation.Keyframe.IntKeyframe;
|
||||
import org.telegram.messenger.Animation.Keyframe.FloatKeyframe;
|
||||
import org.telegram.messenger.Animation.Keyframe.ObjectKeyframe;
|
||||
|
||||
class KeyframeSet {
|
||||
|
||||
int mNumKeyframes;
|
||||
|
||||
Keyframe mFirstKeyframe;
|
||||
Keyframe mLastKeyframe;
|
||||
Interpolator mInterpolator;
|
||||
ArrayList<Keyframe> mKeyframes;
|
||||
TypeEvaluator mEvaluator;
|
||||
|
||||
public KeyframeSet(Keyframe... keyframes) {
|
||||
mNumKeyframes = keyframes.length;
|
||||
mKeyframes = new ArrayList<Keyframe>();
|
||||
mKeyframes.addAll(Arrays.asList(keyframes));
|
||||
mFirstKeyframe = mKeyframes.get(0);
|
||||
mLastKeyframe = mKeyframes.get(mNumKeyframes - 1);
|
||||
mInterpolator = mLastKeyframe.getInterpolator();
|
||||
}
|
||||
|
||||
public static KeyframeSet ofInt(int... values) {
|
||||
int numKeyframes = values.length;
|
||||
IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
|
||||
if (numKeyframes == 1) {
|
||||
keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
|
||||
keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
|
||||
} else {
|
||||
keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
|
||||
for (int i = 1; i < numKeyframes; ++i) {
|
||||
keyframes[i] = (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
|
||||
}
|
||||
}
|
||||
return new IntKeyframeSet(keyframes);
|
||||
}
|
||||
|
||||
public static KeyframeSet ofFloat(float... values) {
|
||||
boolean badValue = false;
|
||||
int numKeyframes = values.length;
|
||||
FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
|
||||
if (numKeyframes == 1) {
|
||||
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
|
||||
keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
|
||||
if (Float.isNaN(values[0])) {
|
||||
badValue = true;
|
||||
}
|
||||
} else {
|
||||
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
|
||||
for (int i = 1; i < numKeyframes; ++i) {
|
||||
keyframes[i] = (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
|
||||
if (Float.isNaN(values[i])) {
|
||||
badValue = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (badValue) {
|
||||
Log.w("Animator", "Bad value (NaN) in float animator");
|
||||
}
|
||||
return new FloatKeyframeSet(keyframes);
|
||||
}
|
||||
|
||||
public static KeyframeSet ofKeyframe(Keyframe... keyframes) {
|
||||
int numKeyframes = keyframes.length;
|
||||
boolean hasFloat = false;
|
||||
boolean hasInt = false;
|
||||
boolean hasOther = false;
|
||||
for (Keyframe keyframe : keyframes) {
|
||||
if (keyframe instanceof FloatKeyframe) {
|
||||
hasFloat = true;
|
||||
} else if (keyframe instanceof IntKeyframe) {
|
||||
hasInt = true;
|
||||
} else {
|
||||
hasOther = true;
|
||||
}
|
||||
}
|
||||
if (hasFloat && !hasInt && !hasOther) {
|
||||
FloatKeyframe floatKeyframes[] = new FloatKeyframe[numKeyframes];
|
||||
for (int i = 0; i < numKeyframes; ++i) {
|
||||
floatKeyframes[i] = (FloatKeyframe) keyframes[i];
|
||||
}
|
||||
return new FloatKeyframeSet(floatKeyframes);
|
||||
} else if (hasInt && !hasFloat && !hasOther) {
|
||||
IntKeyframe intKeyframes[] = new IntKeyframe[numKeyframes];
|
||||
for (int i = 0; i < numKeyframes; ++i) {
|
||||
intKeyframes[i] = (IntKeyframe) keyframes[i];
|
||||
}
|
||||
return new IntKeyframeSet(intKeyframes);
|
||||
} else {
|
||||
return new KeyframeSet(keyframes);
|
||||
}
|
||||
}
|
||||
|
||||
public static KeyframeSet ofObject(Object... values) {
|
||||
int numKeyframes = values.length;
|
||||
ObjectKeyframe keyframes[] = new ObjectKeyframe[Math.max(numKeyframes,2)];
|
||||
if (numKeyframes == 1) {
|
||||
keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f);
|
||||
keyframes[1] = (ObjectKeyframe) Keyframe.ofObject(1f, values[0]);
|
||||
} else {
|
||||
keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f, values[0]);
|
||||
for (int i = 1; i < numKeyframes; ++i) {
|
||||
keyframes[i] = (ObjectKeyframe) Keyframe.ofObject((float) i / (numKeyframes - 1), values[i]);
|
||||
}
|
||||
}
|
||||
return new KeyframeSet(keyframes);
|
||||
}
|
||||
|
||||
public void setEvaluator(TypeEvaluator evaluator) {
|
||||
mEvaluator = evaluator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyframeSet clone() {
|
||||
ArrayList<Keyframe> keyframes = mKeyframes;
|
||||
int numKeyframes = mKeyframes.size();
|
||||
Keyframe[] newKeyframes = new Keyframe[numKeyframes];
|
||||
for (int i = 0; i < numKeyframes; ++i) {
|
||||
newKeyframes[i] = keyframes.get(i).clone();
|
||||
}
|
||||
return new KeyframeSet(newKeyframes);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object getValue(float fraction) {
|
||||
if (mNumKeyframes == 2) {
|
||||
if (mInterpolator != null) {
|
||||
fraction = mInterpolator.getInterpolation(fraction);
|
||||
}
|
||||
return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(), mLastKeyframe.getValue());
|
||||
}
|
||||
if (fraction <= 0f) {
|
||||
final Keyframe nextKeyframe = mKeyframes.get(1);
|
||||
final Interpolator interpolator = nextKeyframe.getInterpolator();
|
||||
if (interpolator != null) {
|
||||
fraction = interpolator.getInterpolation(fraction);
|
||||
}
|
||||
final float prevFraction = mFirstKeyframe.getFraction();
|
||||
float intervalFraction = (fraction - prevFraction) / (nextKeyframe.getFraction() - prevFraction);
|
||||
return mEvaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(), nextKeyframe.getValue());
|
||||
} else if (fraction >= 1f) {
|
||||
final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2);
|
||||
final Interpolator interpolator = mLastKeyframe.getInterpolator();
|
||||
if (interpolator != null) {
|
||||
fraction = interpolator.getInterpolation(fraction);
|
||||
}
|
||||
final float prevFraction = prevKeyframe.getFraction();
|
||||
float intervalFraction = (fraction - prevFraction) / (mLastKeyframe.getFraction() - prevFraction);
|
||||
return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(), mLastKeyframe.getValue());
|
||||
}
|
||||
Keyframe prevKeyframe = mFirstKeyframe;
|
||||
for (int i = 1; i < mNumKeyframes; ++i) {
|
||||
Keyframe nextKeyframe = mKeyframes.get(i);
|
||||
if (fraction < nextKeyframe.getFraction()) {
|
||||
final Interpolator interpolator = nextKeyframe.getInterpolator();
|
||||
if (interpolator != null) {
|
||||
fraction = interpolator.getInterpolation(fraction);
|
||||
}
|
||||
final float prevFraction = prevKeyframe.getFraction();
|
||||
float intervalFraction = (fraction - prevFraction) / (nextKeyframe.getFraction() - prevFraction);
|
||||
return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(), nextKeyframe.getValue());
|
||||
}
|
||||
prevKeyframe = nextKeyframe;
|
||||
}
|
||||
return mLastKeyframe.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String returnVal = " ";
|
||||
for (int i = 0; i < mNumKeyframes; ++i) {
|
||||
returnVal += mKeyframes.get(i).getValue() + " ";
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
}
|
|
@ -1,488 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.Animation;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public final class ObjectAnimator10 extends ValueAnimator {
|
||||
|
||||
private static final HashMap<String, Property> PROXY_PROPERTIES = new HashMap<String, Property>();
|
||||
|
||||
static {
|
||||
Property<View, Float> ALPHA = new FloatProperty10<View>("alpha") {
|
||||
@Override
|
||||
public void setValue(View object, float value) {
|
||||
View10.wrap(object).setAlpha(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(View object) {
|
||||
return View10.wrap(object).getAlpha();
|
||||
}
|
||||
};
|
||||
|
||||
Property<View, Float> PIVOT_X = new FloatProperty10<View>("pivotX") {
|
||||
@Override
|
||||
public void setValue(View object, float value) {
|
||||
View10.wrap(object).setPivotX(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(View object) {
|
||||
return View10.wrap(object).getPivotX();
|
||||
}
|
||||
};
|
||||
|
||||
Property<View, Float> PIVOT_Y = new FloatProperty10<View>("pivotY") {
|
||||
@Override
|
||||
public void setValue(View object, float value) {
|
||||
View10.wrap(object).setPivotY(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(View object) {
|
||||
return View10.wrap(object).getPivotY();
|
||||
}
|
||||
};
|
||||
|
||||
Property<View, Float> TRANSLATION_X = new FloatProperty10<View>("translationX") {
|
||||
@Override
|
||||
public void setValue(View object, float value) {
|
||||
View10.wrap(object).setTranslationX(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(View object) {
|
||||
return View10.wrap(object).getTranslationX();
|
||||
}
|
||||
};
|
||||
|
||||
Property<View, Float> TRANSLATION_Y = new FloatProperty10<View>("translationY") {
|
||||
@Override
|
||||
public void setValue(View object, float value) {
|
||||
View10.wrap(object).setTranslationY(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(View object) {
|
||||
return View10.wrap(object).getTranslationY();
|
||||
}
|
||||
};
|
||||
|
||||
Property<View, Float> ROTATION = new FloatProperty10<View>("rotation") {
|
||||
@Override
|
||||
public void setValue(View object, float value) {
|
||||
View10.wrap(object).setRotation(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(View object) {
|
||||
return View10.wrap(object).getRotation();
|
||||
}
|
||||
};
|
||||
|
||||
Property<View, Float> ROTATION_X = new FloatProperty10<View>("rotationX") {
|
||||
@Override
|
||||
public void setValue(View object, float value) {
|
||||
View10.wrap(object).setRotationX(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(View object) {
|
||||
return View10.wrap(object).getRotationX();
|
||||
}
|
||||
};
|
||||
|
||||
Property<View, Float> ROTATION_Y = new FloatProperty10<View>("rotationY") {
|
||||
@Override
|
||||
public void setValue(View object, float value) {
|
||||
View10.wrap(object).setRotationY(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(View object) {
|
||||
return View10.wrap(object).getRotationY();
|
||||
}
|
||||
};
|
||||
|
||||
Property<View, Float> SCALE_X = new FloatProperty10<View>("scaleX") {
|
||||
@Override
|
||||
public void setValue(View object, float value) {
|
||||
View10.wrap(object).setScaleX(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(View object) {
|
||||
return View10.wrap(object).getScaleX();
|
||||
}
|
||||
};
|
||||
|
||||
Property<View, Float> SCALE_Y = new FloatProperty10<View>("scaleY") {
|
||||
@Override
|
||||
public void setValue(View object, float value) {
|
||||
View10.wrap(object).setScaleY(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(View object) {
|
||||
return View10.wrap(object).getScaleY();
|
||||
}
|
||||
};
|
||||
|
||||
Property<View, Integer> SCROLL_X = new IntProperty<View>("scrollX") {
|
||||
@Override
|
||||
public void setValue(View object, int value) {
|
||||
View10.wrap(object).setScrollX(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer get(View object) {
|
||||
return View10.wrap(object).getScrollX();
|
||||
}
|
||||
};
|
||||
|
||||
Property<View, Integer> SCROLL_Y = new IntProperty<View>("scrollY") {
|
||||
@Override
|
||||
public void setValue(View object, int value) {
|
||||
View10.wrap(object).setScrollY(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer get(View object) {
|
||||
return View10.wrap(object).getScrollY();
|
||||
}
|
||||
};
|
||||
|
||||
Property<View, Float> X = new FloatProperty10<View>("x") {
|
||||
@Override
|
||||
public void setValue(View object, float value) {
|
||||
View10.wrap(object).setX(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(View object) {
|
||||
return View10.wrap(object).getX();
|
||||
}
|
||||
};
|
||||
|
||||
Property<View, Float> Y = new FloatProperty10<View>("y") {
|
||||
@Override
|
||||
public void setValue(View object, float value) {
|
||||
View10.wrap(object).setY(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(View object) {
|
||||
return View10.wrap(object).getY();
|
||||
}
|
||||
};
|
||||
|
||||
PROXY_PROPERTIES.put("alpha", ALPHA);
|
||||
PROXY_PROPERTIES.put("pivotX", PIVOT_X);
|
||||
PROXY_PROPERTIES.put("pivotY", PIVOT_Y);
|
||||
PROXY_PROPERTIES.put("translationX", TRANSLATION_X);
|
||||
PROXY_PROPERTIES.put("translationY", TRANSLATION_Y);
|
||||
PROXY_PROPERTIES.put("rotation", ROTATION);
|
||||
PROXY_PROPERTIES.put("rotationX", ROTATION_X);
|
||||
PROXY_PROPERTIES.put("rotationY", ROTATION_Y);
|
||||
PROXY_PROPERTIES.put("scaleX", SCALE_X);
|
||||
PROXY_PROPERTIES.put("scaleY", SCALE_Y);
|
||||
PROXY_PROPERTIES.put("scrollX", SCROLL_X);
|
||||
PROXY_PROPERTIES.put("scrollY", SCROLL_Y);
|
||||
PROXY_PROPERTIES.put("x", X);
|
||||
PROXY_PROPERTIES.put("y", Y);
|
||||
}
|
||||
|
||||
private Object mTarget;
|
||||
private String mPropertyName;
|
||||
private Property mProperty;
|
||||
private boolean mAutoCancel = false;
|
||||
|
||||
public void setPropertyName(String propertyName) {
|
||||
if (mValues != null) {
|
||||
PropertyValuesHolder valuesHolder = mValues[0];
|
||||
String oldName = valuesHolder.getPropertyName();
|
||||
valuesHolder.setPropertyName(propertyName);
|
||||
mValuesMap.remove(oldName);
|
||||
mValuesMap.put(propertyName, valuesHolder);
|
||||
}
|
||||
mPropertyName = propertyName;
|
||||
mInitialized = false;
|
||||
}
|
||||
|
||||
public void setProperty(Property property) {
|
||||
if (mValues != null) {
|
||||
PropertyValuesHolder valuesHolder = mValues[0];
|
||||
String oldName = valuesHolder.getPropertyName();
|
||||
valuesHolder.setProperty(property);
|
||||
mValuesMap.remove(oldName);
|
||||
mValuesMap.put(mPropertyName, valuesHolder);
|
||||
}
|
||||
if (mProperty != null) {
|
||||
mPropertyName = property.getName();
|
||||
}
|
||||
mProperty = property;
|
||||
mInitialized = false;
|
||||
}
|
||||
|
||||
public String getPropertyName() {
|
||||
String propertyName = null;
|
||||
if (mPropertyName != null) {
|
||||
propertyName = mPropertyName;
|
||||
} else if (mProperty != null) {
|
||||
propertyName = mProperty.getName();
|
||||
} else if (mValues != null && mValues.length > 0) {
|
||||
for (int i = 0; i < mValues.length; ++i) {
|
||||
if (i == 0) {
|
||||
propertyName = "";
|
||||
} else {
|
||||
propertyName += ",";
|
||||
}
|
||||
propertyName += mValues[i].getPropertyName();
|
||||
}
|
||||
}
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
public ObjectAnimator10() {
|
||||
|
||||
}
|
||||
|
||||
private ObjectAnimator10(Object target, String propertyName) {
|
||||
mTarget = target;
|
||||
setPropertyName(propertyName);
|
||||
}
|
||||
|
||||
private <T> ObjectAnimator10(T target, Property<T, ?> property) {
|
||||
mTarget = target;
|
||||
setProperty(property);
|
||||
}
|
||||
|
||||
public static ObjectAnimator10 ofInt(Object target, String propertyName, int... values) {
|
||||
ObjectAnimator10 anim = new ObjectAnimator10(target, propertyName);
|
||||
anim.setIntValues(values);
|
||||
return anim;
|
||||
}
|
||||
|
||||
public static <T> ObjectAnimator10 ofInt(T target, Property<T, Integer> property, int... values) {
|
||||
ObjectAnimator10 anim = new ObjectAnimator10(target, property);
|
||||
anim.setIntValues(values);
|
||||
return anim;
|
||||
}
|
||||
|
||||
public static ObjectAnimator10 ofFloat(Object target, String propertyName, float... values) {
|
||||
ObjectAnimator10 anim = new ObjectAnimator10(target, propertyName);
|
||||
anim.setFloatValues(values);
|
||||
return anim;
|
||||
}
|
||||
|
||||
public static <T> ObjectAnimator10 ofFloat(T target, Property<T, Float> property, float... values) {
|
||||
ObjectAnimator10 anim = new ObjectAnimator10(target, property);
|
||||
anim.setFloatValues(values);
|
||||
return anim;
|
||||
}
|
||||
|
||||
public static ObjectAnimator10 ofObject(Object target, String propertyName, TypeEvaluator evaluator, Object... values) {
|
||||
ObjectAnimator10 anim = new ObjectAnimator10(target, propertyName);
|
||||
anim.setObjectValues(values);
|
||||
anim.setEvaluator(evaluator);
|
||||
return anim;
|
||||
}
|
||||
|
||||
public static <T, V> ObjectAnimator10 ofObject(T target, Property<T, V> property, TypeEvaluator<V> evaluator, V... values) {
|
||||
ObjectAnimator10 anim = new ObjectAnimator10(target, property);
|
||||
anim.setObjectValues(values);
|
||||
anim.setEvaluator(evaluator);
|
||||
return anim;
|
||||
}
|
||||
|
||||
public static ObjectAnimator10 ofPropertyValuesHolder(Object target, PropertyValuesHolder... values) {
|
||||
ObjectAnimator10 anim = new ObjectAnimator10();
|
||||
anim.mTarget = target;
|
||||
anim.setValues(values);
|
||||
return anim;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void setIntValues(int... values) {
|
||||
if (mValues == null || mValues.length == 0) {
|
||||
if (mProperty != null) {
|
||||
setValues(PropertyValuesHolder.ofInt(mProperty, values));
|
||||
} else {
|
||||
setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
|
||||
}
|
||||
} else {
|
||||
super.setIntValues(values);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void setFloatValues(float... values) {
|
||||
if (mValues == null || mValues.length == 0) {
|
||||
if (mProperty != null) {
|
||||
setValues(PropertyValuesHolder.ofFloat(mProperty, values));
|
||||
} else {
|
||||
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
|
||||
}
|
||||
} else {
|
||||
super.setFloatValues(values);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObjectValues(Object... values) {
|
||||
if (mValues == null || mValues.length == 0) {
|
||||
if (mProperty != null) {
|
||||
setValues(PropertyValuesHolder.ofObject(mProperty, null, values));
|
||||
} else {
|
||||
setValues(PropertyValuesHolder.ofObject(mPropertyName, null, values));
|
||||
}
|
||||
} else {
|
||||
super.setObjectValues(values);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAutoCancel(boolean cancel) {
|
||||
mAutoCancel = cancel;
|
||||
}
|
||||
|
||||
private boolean hasSameTargetAndProperties(Animator10 anim) {
|
||||
if (anim instanceof ObjectAnimator10) {
|
||||
PropertyValuesHolder[] theirValues = ((ObjectAnimator10) anim).getValues();
|
||||
if (((ObjectAnimator10) anim).getTarget() == mTarget &&
|
||||
mValues.length == theirValues.length) {
|
||||
for (int i = 0; i < mValues.length; ++i) {
|
||||
PropertyValuesHolder pvhMine = mValues[i];
|
||||
PropertyValuesHolder pvhTheirs = theirValues[i];
|
||||
if (pvhMine.getPropertyName() == null ||
|
||||
!pvhMine.getPropertyName().equals(pvhTheirs.getPropertyName())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
AnimationHandler handler = sAnimationHandler.get();
|
||||
if (handler != null) {
|
||||
int numAnims = handler.mAnimations.size();
|
||||
for (int i = numAnims - 1; i >= 0; i--) {
|
||||
if (handler.mAnimations.get(i) instanceof ObjectAnimator10) {
|
||||
ObjectAnimator10 anim = (ObjectAnimator10) handler.mAnimations.get(i);
|
||||
if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
|
||||
anim.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
numAnims = handler.mPendingAnimations.size();
|
||||
for (int i = numAnims - 1; i >= 0; i--) {
|
||||
if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator10) {
|
||||
ObjectAnimator10 anim = (ObjectAnimator10) handler.mPendingAnimations.get(i);
|
||||
if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
|
||||
anim.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
numAnims = handler.mDelayedAnims.size();
|
||||
for (int i = numAnims - 1; i >= 0; i--) {
|
||||
if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator10) {
|
||||
ObjectAnimator10 anim = (ObjectAnimator10) handler.mDelayedAnims.get(i);
|
||||
if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
|
||||
anim.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
super.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
void initAnimation() {
|
||||
if (!mInitialized) {
|
||||
if ((mProperty == null) && (mTarget instanceof View) && PROXY_PROPERTIES.containsKey(mPropertyName)) {
|
||||
setProperty(PROXY_PROPERTIES.get(mPropertyName));
|
||||
}
|
||||
int numValues = mValues.length;
|
||||
for (PropertyValuesHolder mValue : mValues) {
|
||||
mValue.setupSetterAndGetter(mTarget);
|
||||
}
|
||||
super.initAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectAnimator10 setDuration(long duration) {
|
||||
super.setDuration(duration);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Object getTarget() {
|
||||
return mTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTarget(Object target) {
|
||||
if (mTarget != target) {
|
||||
final Object oldTarget = mTarget;
|
||||
mTarget = target;
|
||||
if (oldTarget != null && target != null && oldTarget.getClass() == target.getClass()) {
|
||||
return;
|
||||
}
|
||||
mInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupStartValues() {
|
||||
initAnimation();
|
||||
int numValues = mValues.length;
|
||||
for (PropertyValuesHolder mValue : mValues) {
|
||||
mValue.setupStartValue(mTarget);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupEndValues() {
|
||||
initAnimation();
|
||||
int numValues = mValues.length;
|
||||
for (PropertyValuesHolder mValue : mValues) {
|
||||
mValue.setupEndValue(mTarget);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void animateValue(float fraction) {
|
||||
super.animateValue(fraction);
|
||||
int numValues = mValues.length;
|
||||
for (PropertyValuesHolder mValue : mValues) {
|
||||
mValue.setAnimatedValue(mTarget);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectAnimator10 clone() {
|
||||
return (ObjectAnimator10) super.clone();
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.Animation;
|
||||
|
||||
public abstract class Property<T, V> {
|
||||
|
||||
private final String mName;
|
||||
private final Class<V> mType;
|
||||
|
||||
public static <T, V> Property<T, V> of(Class<T> hostType, Class<V> valueType, String name) {
|
||||
return new ReflectiveProperty<T, V>(hostType, valueType, name);
|
||||
}
|
||||
|
||||
public Property(Class<V> type, String name) {
|
||||
mName = name;
|
||||
mType = type;
|
||||
}
|
||||
|
||||
public boolean isReadOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void set(T object, V value) {
|
||||
throw new UnsupportedOperationException("Property " + getName() +" is read-only");
|
||||
}
|
||||
|
||||
public abstract V get(T object);
|
||||
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public Class<V> getType() {
|
||||
return mType;
|
||||
}
|
||||
}
|
|
@ -1,545 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.Animation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
public class PropertyValuesHolder implements Cloneable {
|
||||
|
||||
String mPropertyName;
|
||||
protected Property mProperty;
|
||||
Method mSetter = null;
|
||||
private Method mGetter = null;
|
||||
Class mValueType;
|
||||
KeyframeSet mKeyframeSet = null;
|
||||
|
||||
private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
|
||||
private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
|
||||
|
||||
private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class, Double.class, Integer.class};
|
||||
private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class, Float.class, Double.class};
|
||||
private static Class[] DOUBLE_VARIANTS = {double.class, Double.class, float.class, int.class, Float.class, Integer.class};
|
||||
|
||||
private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap = new HashMap<Class, HashMap<String, Method>>();
|
||||
private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap = new HashMap<Class, HashMap<String, Method>>();
|
||||
|
||||
final ReentrantReadWriteLock mPropertyMapLock = new ReentrantReadWriteLock();
|
||||
final Object[] mTmpValueArray = new Object[1];
|
||||
|
||||
private TypeEvaluator mEvaluator;
|
||||
|
||||
private Object mAnimatedValue;
|
||||
|
||||
private PropertyValuesHolder(String propertyName) {
|
||||
mPropertyName = propertyName;
|
||||
}
|
||||
|
||||
private PropertyValuesHolder(Property property) {
|
||||
mProperty = property;
|
||||
if (property != null) {
|
||||
mPropertyName = property.getName();
|
||||
}
|
||||
}
|
||||
|
||||
public static PropertyValuesHolder ofInt(String propertyName, int... values) {
|
||||
return new IntPropertyValuesHolder(propertyName, values);
|
||||
}
|
||||
|
||||
public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) {
|
||||
return new IntPropertyValuesHolder(property, values);
|
||||
}
|
||||
|
||||
public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
|
||||
return new FloatPropertyValuesHolder(propertyName, values);
|
||||
}
|
||||
|
||||
public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
|
||||
return new FloatPropertyValuesHolder(property, values);
|
||||
}
|
||||
|
||||
public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,
|
||||
Object... values) {
|
||||
PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
|
||||
pvh.setObjectValues(values);
|
||||
pvh.setEvaluator(evaluator);
|
||||
return pvh;
|
||||
}
|
||||
|
||||
public static <V> PropertyValuesHolder ofObject(Property property,
|
||||
TypeEvaluator<V> evaluator, V... values) {
|
||||
PropertyValuesHolder pvh = new PropertyValuesHolder(property);
|
||||
pvh.setObjectValues(values);
|
||||
pvh.setEvaluator(evaluator);
|
||||
return pvh;
|
||||
}
|
||||
|
||||
public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) {
|
||||
KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
|
||||
if (keyframeSet instanceof IntKeyframeSet) {
|
||||
return new IntPropertyValuesHolder(propertyName, (IntKeyframeSet) keyframeSet);
|
||||
} else if (keyframeSet instanceof FloatKeyframeSet) {
|
||||
return new FloatPropertyValuesHolder(propertyName, (FloatKeyframeSet) keyframeSet);
|
||||
} else {
|
||||
PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
|
||||
pvh.mKeyframeSet = keyframeSet;
|
||||
pvh.mValueType = values[0].getType();
|
||||
return pvh;
|
||||
}
|
||||
}
|
||||
|
||||
public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) {
|
||||
KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
|
||||
if (keyframeSet instanceof IntKeyframeSet) {
|
||||
return new IntPropertyValuesHolder(property, (IntKeyframeSet) keyframeSet);
|
||||
} else if (keyframeSet instanceof FloatKeyframeSet) {
|
||||
return new FloatPropertyValuesHolder(property, (FloatKeyframeSet) keyframeSet);
|
||||
} else {
|
||||
PropertyValuesHolder pvh = new PropertyValuesHolder(property);
|
||||
pvh.mKeyframeSet = keyframeSet;
|
||||
pvh.mValueType = values[0].getType();
|
||||
return pvh;
|
||||
}
|
||||
}
|
||||
|
||||
public void setIntValues(int... values) {
|
||||
mValueType = int.class;
|
||||
mKeyframeSet = KeyframeSet.ofInt(values);
|
||||
}
|
||||
|
||||
public void setFloatValues(float... values) {
|
||||
mValueType = float.class;
|
||||
mKeyframeSet = KeyframeSet.ofFloat(values);
|
||||
}
|
||||
|
||||
public void setKeyframes(Keyframe... values) {
|
||||
int numKeyframes = values.length;
|
||||
Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes, 2)];
|
||||
mValueType = values[0].getType();
|
||||
System.arraycopy(values, 0, keyframes, 0, numKeyframes);
|
||||
mKeyframeSet = new KeyframeSet(keyframes);
|
||||
}
|
||||
|
||||
public void setObjectValues(Object... values) {
|
||||
mValueType = values[0].getClass();
|
||||
mKeyframeSet = KeyframeSet.ofObject(values);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
|
||||
Method returnVal = null;
|
||||
String methodName = getMethodName(prefix, mPropertyName);
|
||||
Class args[] = null;
|
||||
if (valueType == null) {
|
||||
try {
|
||||
returnVal = targetClass.getMethod(methodName);
|
||||
} catch (Throwable e) {
|
||||
try {
|
||||
returnVal = targetClass.getDeclaredMethod(methodName);
|
||||
returnVal.setAccessible(true);
|
||||
} catch (Throwable e2) {
|
||||
e2.printStackTrace();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
args = new Class[1];
|
||||
Class typeVariants[];
|
||||
if (mValueType.equals(Float.class)) {
|
||||
typeVariants = FLOAT_VARIANTS;
|
||||
} else if (mValueType.equals(Integer.class)) {
|
||||
typeVariants = INTEGER_VARIANTS;
|
||||
} else if (mValueType.equals(Double.class)) {
|
||||
typeVariants = DOUBLE_VARIANTS;
|
||||
} else {
|
||||
typeVariants = new Class[1];
|
||||
typeVariants[0] = mValueType;
|
||||
}
|
||||
for (Class typeVariant : typeVariants) {
|
||||
args[0] = typeVariant;
|
||||
try {
|
||||
returnVal = targetClass.getMethod(methodName, args);
|
||||
mValueType = typeVariant;
|
||||
return returnVal;
|
||||
} catch (Throwable e) {
|
||||
try {
|
||||
returnVal = targetClass.getDeclaredMethod(methodName, args);
|
||||
returnVal.setAccessible(true);
|
||||
mValueType = typeVariant;
|
||||
return returnVal;
|
||||
} catch (Throwable e2) {
|
||||
// Swallow the error and keep trying other variants
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
private Method setupSetterOrGetter(Class targetClass, HashMap<Class, HashMap<String, Method>> propertyMapMap, String prefix, Class valueType) {
|
||||
Method setterOrGetter = null;
|
||||
try {
|
||||
mPropertyMapLock.writeLock().lock();
|
||||
HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
|
||||
if (propertyMap != null) {
|
||||
setterOrGetter = propertyMap.get(mPropertyName);
|
||||
}
|
||||
if (setterOrGetter == null) {
|
||||
setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
|
||||
if (propertyMap == null) {
|
||||
propertyMap = new HashMap<String, Method>();
|
||||
propertyMapMap.put(targetClass, propertyMap);
|
||||
}
|
||||
propertyMap.put(mPropertyName, setterOrGetter);
|
||||
}
|
||||
} finally {
|
||||
mPropertyMapLock.writeLock().unlock();
|
||||
}
|
||||
return setterOrGetter;
|
||||
}
|
||||
|
||||
void setupSetter(Class targetClass) {
|
||||
mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType);
|
||||
}
|
||||
|
||||
private void setupGetter(Class targetClass) {
|
||||
mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
void setupSetterAndGetter(Object target) {
|
||||
if (mProperty != null) {
|
||||
try {
|
||||
Object testValue = mProperty.get(target);
|
||||
for (Keyframe kf : mKeyframeSet.mKeyframes) {
|
||||
if (!kf.hasValue()) {
|
||||
kf.setValue(mProperty.get(target));
|
||||
}
|
||||
}
|
||||
return;
|
||||
} catch (Throwable e) {
|
||||
mProperty = null;
|
||||
}
|
||||
}
|
||||
Class targetClass = target.getClass();
|
||||
if (mSetter == null) {
|
||||
setupSetter(targetClass);
|
||||
}
|
||||
for (Keyframe kf : mKeyframeSet.mKeyframes) {
|
||||
if (!kf.hasValue()) {
|
||||
if (mGetter == null) {
|
||||
setupGetter(targetClass);
|
||||
if (mGetter == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
kf.setValue(mGetter.invoke(target));
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void setupValue(Object target, Keyframe kf) {
|
||||
if (mProperty != null) {
|
||||
kf.setValue(mProperty.get(target));
|
||||
}
|
||||
try {
|
||||
if (mGetter == null) {
|
||||
Class targetClass = target.getClass();
|
||||
setupGetter(targetClass);
|
||||
if (mGetter == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
kf.setValue(mGetter.invoke(target));
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
void setupStartValue(Object target) {
|
||||
setupValue(target, mKeyframeSet.mKeyframes.get(0));
|
||||
}
|
||||
|
||||
void setupEndValue(Object target) {
|
||||
setupValue(target, mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyValuesHolder clone() {
|
||||
try {
|
||||
PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
|
||||
newPVH.mPropertyName = mPropertyName;
|
||||
newPVH.mProperty = mProperty;
|
||||
newPVH.mKeyframeSet = mKeyframeSet.clone();
|
||||
newPVH.mEvaluator = mEvaluator;
|
||||
return newPVH;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
void setAnimatedValue(Object target) {
|
||||
if (mProperty != null) {
|
||||
mProperty.set(target, getAnimatedValue());
|
||||
}
|
||||
if (mSetter != null) {
|
||||
try {
|
||||
mTmpValueArray[0] = getAnimatedValue();
|
||||
mSetter.invoke(target, mTmpValueArray);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init() {
|
||||
if (mEvaluator == null) {
|
||||
mEvaluator = (mValueType == Integer.class) ? sIntEvaluator : (mValueType == Float.class) ? sFloatEvaluator : null;
|
||||
}
|
||||
if (mEvaluator != null) {
|
||||
mKeyframeSet.setEvaluator(mEvaluator);
|
||||
}
|
||||
}
|
||||
|
||||
public void setEvaluator(TypeEvaluator evaluator) {
|
||||
mEvaluator = evaluator;
|
||||
mKeyframeSet.setEvaluator(evaluator);
|
||||
}
|
||||
|
||||
void calculateValue(float fraction) {
|
||||
mAnimatedValue = mKeyframeSet.getValue(fraction);
|
||||
}
|
||||
|
||||
public void setPropertyName(String propertyName) {
|
||||
mPropertyName = propertyName;
|
||||
}
|
||||
|
||||
public void setProperty(Property property) {
|
||||
mProperty = property;
|
||||
}
|
||||
|
||||
public String getPropertyName() {
|
||||
return mPropertyName;
|
||||
}
|
||||
|
||||
Object getAnimatedValue() {
|
||||
return mAnimatedValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mPropertyName + ": " + mKeyframeSet.toString();
|
||||
}
|
||||
|
||||
static String getMethodName(String prefix, String propertyName) {
|
||||
if (propertyName == null || propertyName.length() == 0) {
|
||||
return prefix;
|
||||
}
|
||||
char firstLetter = Character.toUpperCase(propertyName.charAt(0));
|
||||
String theRest = propertyName.substring(1);
|
||||
return prefix + firstLetter + theRest;
|
||||
}
|
||||
|
||||
static class IntPropertyValuesHolder extends PropertyValuesHolder {
|
||||
private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap = new HashMap<Class, HashMap<String, Integer>>();
|
||||
private IntProperty mIntProperty;
|
||||
|
||||
IntKeyframeSet mIntKeyframeSet;
|
||||
int mIntAnimatedValue;
|
||||
|
||||
public IntPropertyValuesHolder(String propertyName, IntKeyframeSet keyframeSet) {
|
||||
super(propertyName);
|
||||
mValueType = int.class;
|
||||
mKeyframeSet = keyframeSet;
|
||||
mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
|
||||
}
|
||||
|
||||
public IntPropertyValuesHolder(Property property, IntKeyframeSet keyframeSet) {
|
||||
super(property);
|
||||
mValueType = int.class;
|
||||
mKeyframeSet = keyframeSet;
|
||||
mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
|
||||
if (property instanceof IntProperty) {
|
||||
mIntProperty = (IntProperty) mProperty;
|
||||
}
|
||||
}
|
||||
|
||||
public IntPropertyValuesHolder(String propertyName, int... values) {
|
||||
super(propertyName);
|
||||
setIntValues(values);
|
||||
}
|
||||
|
||||
public IntPropertyValuesHolder(Property property, int... values) {
|
||||
super(property);
|
||||
setIntValues(values);
|
||||
if (property instanceof IntProperty) {
|
||||
mIntProperty = (IntProperty) mProperty;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIntValues(int... values) {
|
||||
super.setIntValues(values);
|
||||
mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
void calculateValue(float fraction) {
|
||||
mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction);
|
||||
}
|
||||
|
||||
@Override
|
||||
Object getAnimatedValue() {
|
||||
return mIntAnimatedValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntPropertyValuesHolder clone() {
|
||||
IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super.clone();
|
||||
newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet;
|
||||
return newPVH;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
void setAnimatedValue(Object target) {
|
||||
if (mIntProperty != null) {
|
||||
mIntProperty.setValue(target, mIntAnimatedValue);
|
||||
return;
|
||||
}
|
||||
if (mProperty != null) {
|
||||
mProperty.set(target, mIntAnimatedValue);
|
||||
return;
|
||||
}
|
||||
if (mSetter != null) {
|
||||
try {
|
||||
mTmpValueArray[0] = mIntAnimatedValue;
|
||||
mSetter.invoke(target, mTmpValueArray);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void setupSetter(Class targetClass) {
|
||||
if (mProperty != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.setupSetter(targetClass);
|
||||
}
|
||||
}
|
||||
|
||||
static class FloatPropertyValuesHolder extends PropertyValuesHolder {
|
||||
|
||||
private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap = new HashMap<Class, HashMap<String, Integer>>();
|
||||
private FloatProperty10 mFloatProperty;
|
||||
|
||||
FloatKeyframeSet mFloatKeyframeSet;
|
||||
float mFloatAnimatedValue;
|
||||
|
||||
public FloatPropertyValuesHolder(String propertyName, FloatKeyframeSet keyframeSet) {
|
||||
super(propertyName);
|
||||
mValueType = float.class;
|
||||
mKeyframeSet = keyframeSet;
|
||||
mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
|
||||
}
|
||||
|
||||
public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) {
|
||||
super(property);
|
||||
mValueType = float.class;
|
||||
mKeyframeSet = keyframeSet;
|
||||
mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
|
||||
if (property instanceof FloatProperty10) {
|
||||
mFloatProperty = (FloatProperty10) mProperty;
|
||||
}
|
||||
}
|
||||
|
||||
public FloatPropertyValuesHolder(String propertyName, float... values) {
|
||||
super(propertyName);
|
||||
setFloatValues(values);
|
||||
}
|
||||
|
||||
public FloatPropertyValuesHolder(Property property, float... values) {
|
||||
super(property);
|
||||
setFloatValues(values);
|
||||
if (property instanceof FloatProperty10) {
|
||||
mFloatProperty = (FloatProperty10) mProperty;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFloatValues(float... values) {
|
||||
super.setFloatValues(values);
|
||||
mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
void calculateValue(float fraction) {
|
||||
mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);
|
||||
}
|
||||
|
||||
@Override
|
||||
Object getAnimatedValue() {
|
||||
return mFloatAnimatedValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatPropertyValuesHolder clone() {
|
||||
FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super.clone();
|
||||
newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet;
|
||||
return newPVH;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
void setAnimatedValue(Object target) {
|
||||
if (mFloatProperty != null) {
|
||||
mFloatProperty.setValue(target, mFloatAnimatedValue);
|
||||
return;
|
||||
}
|
||||
if (mProperty != null) {
|
||||
mProperty.set(target, mFloatAnimatedValue);
|
||||
return;
|
||||
}
|
||||
if (mSetter != null) {
|
||||
try {
|
||||
mTmpValueArray[0] = mFloatAnimatedValue;
|
||||
mSetter.invoke(target, mTmpValueArray);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void setupSetter(Class targetClass) {
|
||||
if (mProperty != null) {
|
||||
return;
|
||||
}
|
||||
super.setupSetter(targetClass);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,182 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.Animation;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Internal class to automatically generate a Property for a given class/name pair, given the
|
||||
* specification of {@link Property#of(java.lang.Class, java.lang.Class, java.lang.String)}
|
||||
*/
|
||||
class ReflectiveProperty<T, V> extends Property<T, V> {
|
||||
|
||||
private static final String PREFIX_GET = "get";
|
||||
private static final String PREFIX_IS = "is";
|
||||
private static final String PREFIX_SET = "set";
|
||||
private Method mSetter;
|
||||
private Method mGetter;
|
||||
private Field mField;
|
||||
|
||||
/**
|
||||
* For given property name 'name', look for getName/isName method or 'name' field.
|
||||
* Also look for setName method (optional - could be readonly). Failing method getters and
|
||||
* field results in throwing NoSuchPropertyException.
|
||||
*
|
||||
* @param propertyHolder The class on which the methods or field are found
|
||||
* @param name The name of the property, where this name is capitalized and appended to
|
||||
* "get" and "is to search for the appropriate methods. If the get/is methods are not found,
|
||||
* the constructor will search for a field with that exact name.
|
||||
*/
|
||||
public ReflectiveProperty(Class<T> propertyHolder, Class<V> valueType, String name) {
|
||||
// TODO: cache reflection info for each new class/name pair
|
||||
super(valueType, name);
|
||||
char firstLetter = Character.toUpperCase(name.charAt(0));
|
||||
String theRest = name.substring(1);
|
||||
String capitalizedName = firstLetter + theRest;
|
||||
String getterName = PREFIX_GET + capitalizedName;
|
||||
try {
|
||||
mGetter = propertyHolder.getMethod(getterName, (Class<?>[]) null);
|
||||
} catch (NoSuchMethodException e) {
|
||||
try {
|
||||
/* The native implementation uses JNI to do reflection, which allows access to private methods.
|
||||
* getDeclaredMethod(..) does not find superclass methods, so it's implemented as a fallback.
|
||||
*/
|
||||
mGetter = propertyHolder.getDeclaredMethod(getterName, (Class<?>[]) null);
|
||||
mGetter.setAccessible(true);
|
||||
} catch (NoSuchMethodException e2) {
|
||||
// getName() not available - try isName() instead
|
||||
getterName = PREFIX_IS + capitalizedName;
|
||||
try {
|
||||
mGetter = propertyHolder.getMethod(getterName, (Class<?>[]) null);
|
||||
} catch (NoSuchMethodException e3) {
|
||||
try {
|
||||
/* The native implementation uses JNI to do reflection, which allows access to private methods.
|
||||
* getDeclaredMethod(..) does not find superclass methods, so it's implemented as a fallback.
|
||||
*/
|
||||
mGetter = propertyHolder.getDeclaredMethod(getterName, (Class<?>[]) null);
|
||||
mGetter.setAccessible(true);
|
||||
} catch (NoSuchMethodException e4) {
|
||||
// Try public field instead
|
||||
try {
|
||||
mField = propertyHolder.getField(name);
|
||||
Class fieldType = mField.getType();
|
||||
if (!typesMatch(valueType, fieldType)) {
|
||||
throw new NoSuchPropertyException("Underlying type (" + fieldType + ") " +
|
||||
"does not match Property type (" + valueType + ")");
|
||||
}
|
||||
return;
|
||||
} catch (NoSuchFieldException e5) {
|
||||
// no way to access property - throw appropriate exception
|
||||
throw new NoSuchPropertyException("No accessor method or field found for"
|
||||
+ " property with name " + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Class getterType = mGetter.getReturnType();
|
||||
// Check to make sure our getter type matches our valueType
|
||||
if (!typesMatch(valueType, getterType)) {
|
||||
throw new NoSuchPropertyException("Underlying type (" + getterType + ") " +
|
||||
"does not match Property type (" + valueType + ")");
|
||||
}
|
||||
String setterName = PREFIX_SET + capitalizedName;
|
||||
try {
|
||||
// mSetter = propertyHolder.getMethod(setterName, getterType);
|
||||
// The native implementation uses JNI to do reflection, which allows access to private methods.
|
||||
mSetter = propertyHolder.getDeclaredMethod(setterName, getterType);
|
||||
mSetter.setAccessible(true);
|
||||
} catch (NoSuchMethodException ignored) {
|
||||
// Okay to not have a setter - just a readonly property
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to check whether the type of the underlying field/method on the target
|
||||
* object matches the type of the Property. The extra checks for primitive types are because
|
||||
* generics will force the Property type to be a class, whereas the type of the underlying
|
||||
* method/field will probably be a primitive type instead. Accept float as matching Float,
|
||||
* etc.
|
||||
*/
|
||||
private boolean typesMatch(Class<V> valueType, Class getterType) {
|
||||
if (getterType != valueType) {
|
||||
if (getterType.isPrimitive()) {
|
||||
return (getterType == float.class && valueType == Float.class) ||
|
||||
(getterType == int.class && valueType == Integer.class) ||
|
||||
(getterType == boolean.class && valueType == Boolean.class) ||
|
||||
(getterType == long.class && valueType == Long.class) ||
|
||||
(getterType == double.class && valueType == Double.class) ||
|
||||
(getterType == short.class && valueType == Short.class) ||
|
||||
(getterType == byte.class && valueType == Byte.class) ||
|
||||
(getterType == char.class && valueType == Character.class);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T object, V value) {
|
||||
if (mSetter != null) {
|
||||
try {
|
||||
mSetter.invoke(object, value);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError();
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e.getCause());
|
||||
}
|
||||
} else if (mField != null) {
|
||||
try {
|
||||
mField.set(object, value);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Property " + getName() +" is read-only");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(T object) {
|
||||
if (mGetter != null) {
|
||||
try {
|
||||
return (V) mGetter.invoke(object, (Object[])null);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError();
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e.getCause());
|
||||
}
|
||||
} else if (mField != null) {
|
||||
try {
|
||||
return (V) mField.get(object);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
// Should not get here: there should always be a non-null getter or field
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false if there is no setter or public field underlying this Property.
|
||||
*/
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return (mSetter == null && mField == null);
|
||||
}
|
||||
}
|
|
@ -1,675 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.Animation;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.util.AndroidRuntimeException;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
|
||||
import org.telegram.messenger.AndroidUtilities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class ValueAnimator extends Animator10 {
|
||||
|
||||
private static float sDurationScale = 1.0f;
|
||||
static final int STOPPED = 0;
|
||||
static final int RUNNING = 1;
|
||||
static final int SEEKED = 2;
|
||||
|
||||
long mStartTime;
|
||||
long mSeekTime = -1;
|
||||
private long mPauseTime;
|
||||
private boolean mResumed = false;
|
||||
protected static ThreadLocal<AnimationHandler> sAnimationHandler = new ThreadLocal<AnimationHandler>();
|
||||
private static final Interpolator sDefaultInterpolator = new AccelerateDecelerateInterpolator();
|
||||
private boolean mPlayingBackwards = false;
|
||||
private int mCurrentIteration = 0;
|
||||
private float mCurrentFraction = 0f;
|
||||
private boolean mStartedDelay = false;
|
||||
private long mDelayStartTime;
|
||||
int mPlayingState = STOPPED;
|
||||
private boolean mRunning = false;
|
||||
private boolean mStarted = false;
|
||||
private boolean mStartListenersCalled = false;
|
||||
boolean mInitialized = false;
|
||||
|
||||
private long mDuration = (long)(300 * sDurationScale);
|
||||
private long mUnscaledDuration = 300;
|
||||
private long mStartDelay = 0;
|
||||
private long mUnscaledStartDelay = 0;
|
||||
private int mRepeatCount = 0;
|
||||
private int mRepeatMode = RESTART;
|
||||
private Interpolator mInterpolator = sDefaultInterpolator;
|
||||
private ArrayList<AnimatorUpdateListener> mUpdateListeners = null;
|
||||
PropertyValuesHolder[] mValues;
|
||||
HashMap<String, PropertyValuesHolder> mValuesMap;
|
||||
|
||||
public static final int RESTART = 1;
|
||||
public static final int REVERSE = 2;
|
||||
public static final int INFINITE = -1;
|
||||
|
||||
public static void setDurationScale(float durationScale) {
|
||||
sDurationScale = durationScale;
|
||||
}
|
||||
|
||||
public static float getDurationScale() {
|
||||
return sDurationScale;
|
||||
}
|
||||
|
||||
public ValueAnimator() {
|
||||
|
||||
}
|
||||
|
||||
public static ValueAnimator ofInt(int... values) {
|
||||
ValueAnimator anim = new ValueAnimator();
|
||||
anim.setIntValues(values);
|
||||
return anim;
|
||||
}
|
||||
|
||||
public static ValueAnimator ofFloat(float... values) {
|
||||
ValueAnimator anim = new ValueAnimator();
|
||||
anim.setFloatValues(values);
|
||||
return anim;
|
||||
}
|
||||
|
||||
public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values) {
|
||||
ValueAnimator anim = new ValueAnimator();
|
||||
anim.setValues(values);
|
||||
return anim;
|
||||
}
|
||||
|
||||
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) {
|
||||
ValueAnimator anim = new ValueAnimator();
|
||||
anim.setObjectValues(values);
|
||||
anim.setEvaluator(evaluator);
|
||||
return anim;
|
||||
}
|
||||
|
||||
public void setIntValues(int... values) {
|
||||
if (values == null || values.length == 0) {
|
||||
return;
|
||||
}
|
||||
if (mValues == null || mValues.length == 0) {
|
||||
setValues(PropertyValuesHolder.ofInt("", values));
|
||||
} else {
|
||||
PropertyValuesHolder valuesHolder = mValues[0];
|
||||
valuesHolder.setIntValues(values);
|
||||
}
|
||||
mInitialized = false;
|
||||
}
|
||||
|
||||
public void setFloatValues(float... values) {
|
||||
if (values == null || values.length == 0) {
|
||||
return;
|
||||
}
|
||||
if (mValues == null || mValues.length == 0) {
|
||||
setValues(PropertyValuesHolder.ofFloat("", values));
|
||||
} else {
|
||||
PropertyValuesHolder valuesHolder = mValues[0];
|
||||
valuesHolder.setFloatValues(values);
|
||||
}
|
||||
mInitialized = false;
|
||||
}
|
||||
|
||||
public void setObjectValues(Object... values) {
|
||||
if (values == null || values.length == 0) {
|
||||
return;
|
||||
}
|
||||
if (mValues == null || mValues.length == 0) {
|
||||
setValues(PropertyValuesHolder.ofObject("", null, values));
|
||||
} else {
|
||||
PropertyValuesHolder valuesHolder = mValues[0];
|
||||
valuesHolder.setObjectValues(values);
|
||||
}
|
||||
mInitialized = false;
|
||||
}
|
||||
|
||||
public void setValues(PropertyValuesHolder... values) {
|
||||
int numValues = values.length;
|
||||
mValues = values;
|
||||
mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
|
||||
for (PropertyValuesHolder valuesHolder : values) {
|
||||
mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
|
||||
}
|
||||
mInitialized = false;
|
||||
}
|
||||
|
||||
public PropertyValuesHolder[] getValues() {
|
||||
return mValues;
|
||||
}
|
||||
|
||||
void initAnimation() {
|
||||
if (!mInitialized) {
|
||||
int numValues = mValues.length;
|
||||
for (PropertyValuesHolder mValue : mValues) {
|
||||
mValue.init();
|
||||
}
|
||||
mInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public ValueAnimator setDuration(long duration) {
|
||||
if (duration < 0) {
|
||||
throw new IllegalArgumentException("Animators cannot have negative duration: " + duration);
|
||||
}
|
||||
mUnscaledDuration = duration;
|
||||
mDuration = (long)(duration * sDurationScale);
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getDuration() {
|
||||
return mUnscaledDuration;
|
||||
}
|
||||
|
||||
public void setCurrentPlayTime(long playTime) {
|
||||
initAnimation();
|
||||
long currentTime = AnimationUtils.currentAnimationTimeMillis();
|
||||
if (mPlayingState != RUNNING) {
|
||||
mSeekTime = playTime;
|
||||
mPlayingState = SEEKED;
|
||||
}
|
||||
mStartTime = currentTime - playTime;
|
||||
doAnimationFrame(currentTime);
|
||||
}
|
||||
|
||||
public long getCurrentPlayTime() {
|
||||
if (!mInitialized || mPlayingState == STOPPED) {
|
||||
return 0;
|
||||
}
|
||||
return AnimationUtils.currentAnimationTimeMillis() - mStartTime;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static class AnimationHandler implements Runnable {
|
||||
|
||||
protected final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();
|
||||
private final ArrayList<ValueAnimator> mTmpAnimations = new ArrayList<ValueAnimator>();
|
||||
protected final ArrayList<ValueAnimator> mPendingAnimations = new ArrayList<ValueAnimator>();
|
||||
protected final ArrayList<ValueAnimator> mDelayedAnims = new ArrayList<ValueAnimator>();
|
||||
private final ArrayList<ValueAnimator> mEndingAnims = new ArrayList<ValueAnimator>();
|
||||
private final ArrayList<ValueAnimator> mReadyAnims = new ArrayList<ValueAnimator>();
|
||||
|
||||
private boolean mAnimationScheduled;
|
||||
|
||||
public void start() {
|
||||
scheduleAnimation();
|
||||
}
|
||||
|
||||
private void doAnimationFrame(long frameTime) {
|
||||
while (mPendingAnimations.size() > 0) {
|
||||
ArrayList<ValueAnimator> pendingCopy = (ArrayList<ValueAnimator>) mPendingAnimations.clone();
|
||||
mPendingAnimations.clear();
|
||||
int count = pendingCopy.size();
|
||||
for (ValueAnimator anim : pendingCopy) {
|
||||
if (anim.mStartDelay == 0) {
|
||||
anim.startAnimation(this);
|
||||
} else {
|
||||
mDelayedAnims.add(anim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int numDelayedAnims = mDelayedAnims.size();
|
||||
for (ValueAnimator anim : mDelayedAnims) {
|
||||
if (anim.delayedAnimationFrame(frameTime)) {
|
||||
mReadyAnims.add(anim);
|
||||
}
|
||||
}
|
||||
int numReadyAnims = mReadyAnims.size();
|
||||
if (numReadyAnims > 0) {
|
||||
for (ValueAnimator anim : mReadyAnims) {
|
||||
anim.startAnimation(this);
|
||||
anim.mRunning = true;
|
||||
mDelayedAnims.remove(anim);
|
||||
}
|
||||
mReadyAnims.clear();
|
||||
}
|
||||
|
||||
int numAnims = mAnimations.size();
|
||||
for (ValueAnimator mAnimation : mAnimations) {
|
||||
mTmpAnimations.add(mAnimation);
|
||||
}
|
||||
for (int i = 0; i < numAnims; ++i) {
|
||||
ValueAnimator anim = mTmpAnimations.get(i);
|
||||
if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
|
||||
mEndingAnims.add(anim);
|
||||
}
|
||||
}
|
||||
mTmpAnimations.clear();
|
||||
if (mEndingAnims.size() > 0) {
|
||||
for (ValueAnimator mEndingAnim : mEndingAnims) {
|
||||
mEndingAnim.endAnimation(this);
|
||||
}
|
||||
mEndingAnims.clear();
|
||||
}
|
||||
|
||||
if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
|
||||
scheduleAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mAnimationScheduled = false;
|
||||
doAnimationFrame(System.nanoTime() / 1000000);
|
||||
}
|
||||
|
||||
private void scheduleAnimation() {
|
||||
if (!mAnimationScheduled) {
|
||||
AndroidUtilities.runOnUIThread(this);
|
||||
mAnimationScheduled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long getStartDelay() {
|
||||
return mUnscaledStartDelay;
|
||||
}
|
||||
|
||||
public void setStartDelay(long startDelay) {
|
||||
this.mStartDelay = (long)(startDelay * sDurationScale);
|
||||
mUnscaledStartDelay = startDelay;
|
||||
}
|
||||
|
||||
public Object getAnimatedValue() {
|
||||
if (mValues != null && mValues.length > 0) {
|
||||
return mValues[0].getAnimatedValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object getAnimatedValue(String propertyName) {
|
||||
PropertyValuesHolder valuesHolder = mValuesMap.get(propertyName);
|
||||
if (valuesHolder != null) {
|
||||
return valuesHolder.getAnimatedValue();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setRepeatCount(int value) {
|
||||
mRepeatCount = value;
|
||||
}
|
||||
|
||||
public int getRepeatCount() {
|
||||
return mRepeatCount;
|
||||
}
|
||||
|
||||
public void setRepeatMode(int value) {
|
||||
mRepeatMode = value;
|
||||
}
|
||||
|
||||
public int getRepeatMode() {
|
||||
return mRepeatMode;
|
||||
}
|
||||
|
||||
public void addUpdateListener(AnimatorUpdateListener listener) {
|
||||
if (mUpdateListeners == null) {
|
||||
mUpdateListeners = new ArrayList<AnimatorUpdateListener>();
|
||||
}
|
||||
mUpdateListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeAllUpdateListeners() {
|
||||
if (mUpdateListeners == null) {
|
||||
return;
|
||||
}
|
||||
mUpdateListeners.clear();
|
||||
mUpdateListeners = null;
|
||||
}
|
||||
|
||||
public void removeUpdateListener(AnimatorUpdateListener listener) {
|
||||
if (mUpdateListeners == null) {
|
||||
return;
|
||||
}
|
||||
mUpdateListeners.remove(listener);
|
||||
if (mUpdateListeners.size() == 0) {
|
||||
mUpdateListeners = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInterpolator(Interpolator value) {
|
||||
if (value != null) {
|
||||
mInterpolator = value;
|
||||
} else {
|
||||
mInterpolator = new LinearInterpolator();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Interpolator getInterpolator() {
|
||||
return mInterpolator;
|
||||
}
|
||||
|
||||
public void setEvaluator(TypeEvaluator value) {
|
||||
if (value != null && mValues != null && mValues.length > 0) {
|
||||
mValues[0].setEvaluator(value);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void notifyStartListeners() {
|
||||
if (mListeners != null && !mStartListenersCalled) {
|
||||
ArrayList<AnimatorListener> tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
|
||||
int numListeners = tmpListeners.size();
|
||||
for (AnimatorListener tmpListener : tmpListeners) {
|
||||
tmpListener.onAnimationStart(this);
|
||||
}
|
||||
}
|
||||
mStartListenersCalled = true;
|
||||
}
|
||||
|
||||
private void start(boolean playBackwards) {
|
||||
if (Looper.myLooper() == null) {
|
||||
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
|
||||
}
|
||||
mPlayingBackwards = playBackwards;
|
||||
mCurrentIteration = 0;
|
||||
mPlayingState = STOPPED;
|
||||
mStarted = true;
|
||||
mStartedDelay = false;
|
||||
mPaused = false;
|
||||
AnimationHandler animationHandler = getOrCreateAnimationHandler();
|
||||
animationHandler.mPendingAnimations.add(this);
|
||||
if (mStartDelay == 0) {
|
||||
setCurrentPlayTime(0);
|
||||
mPlayingState = STOPPED;
|
||||
mRunning = true;
|
||||
notifyStartListeners();
|
||||
}
|
||||
animationHandler.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
start(false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void cancel() {
|
||||
AnimationHandler handler = getOrCreateAnimationHandler();
|
||||
if (mPlayingState != STOPPED || handler.mPendingAnimations.contains(this) || handler.mDelayedAnims.contains(this)) {
|
||||
if ((mStarted || mRunning) && mListeners != null) {
|
||||
if (!mRunning) {
|
||||
notifyStartListeners();
|
||||
}
|
||||
ArrayList<AnimatorListener> tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
|
||||
for (AnimatorListener listener : tmpListeners) {
|
||||
listener.onAnimationCancel(this);
|
||||
}
|
||||
}
|
||||
endAnimation(handler);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
AnimationHandler handler = getOrCreateAnimationHandler();
|
||||
if (!handler.mAnimations.contains(this) && !handler.mPendingAnimations.contains(this)) {
|
||||
mStartedDelay = false;
|
||||
startAnimation(handler);
|
||||
mStarted = true;
|
||||
} else if (!mInitialized) {
|
||||
initAnimation();
|
||||
}
|
||||
animateValue(mPlayingBackwards ? 0f : 1f);
|
||||
endAnimation(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume() {
|
||||
if (mPaused) {
|
||||
mResumed = true;
|
||||
}
|
||||
super.resume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause() {
|
||||
boolean previouslyPaused = mPaused;
|
||||
super.pause();
|
||||
if (!previouslyPaused && mPaused) {
|
||||
mPauseTime = -1;
|
||||
mResumed = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return (mPlayingState == RUNNING || mRunning);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStarted() {
|
||||
return mStarted;
|
||||
}
|
||||
|
||||
public void reverse() {
|
||||
mPlayingBackwards = !mPlayingBackwards;
|
||||
if (mPlayingState == RUNNING) {
|
||||
long currentTime = AnimationUtils.currentAnimationTimeMillis();
|
||||
long currentPlayTime = currentTime - mStartTime;
|
||||
long timeLeft = mDuration - currentPlayTime;
|
||||
mStartTime = currentTime - timeLeft;
|
||||
} else if (mStarted) {
|
||||
end();
|
||||
} else {
|
||||
start(true);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void endAnimation(AnimationHandler handler) {
|
||||
handler.mAnimations.remove(this);
|
||||
handler.mPendingAnimations.remove(this);
|
||||
handler.mDelayedAnims.remove(this);
|
||||
mPlayingState = STOPPED;
|
||||
mPaused = false;
|
||||
if ((mStarted || mRunning) && mListeners != null) {
|
||||
if (!mRunning) {
|
||||
notifyStartListeners();
|
||||
}
|
||||
ArrayList<AnimatorListener> tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
|
||||
int numListeners = tmpListeners.size();
|
||||
for (AnimatorListener tmpListener : tmpListeners) {
|
||||
tmpListener.onAnimationEnd(this);
|
||||
}
|
||||
}
|
||||
mRunning = false;
|
||||
mStarted = false;
|
||||
mStartListenersCalled = false;
|
||||
mPlayingBackwards = false;
|
||||
}
|
||||
|
||||
private void startAnimation(AnimationHandler handler) {
|
||||
initAnimation();
|
||||
handler.mAnimations.add(this);
|
||||
if (mStartDelay > 0 && mListeners != null) {
|
||||
notifyStartListeners();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean delayedAnimationFrame(long currentTime) {
|
||||
if (!mStartedDelay) {
|
||||
mStartedDelay = true;
|
||||
mDelayStartTime = currentTime;
|
||||
} else {
|
||||
if (mPaused) {
|
||||
if (mPauseTime < 0) {
|
||||
mPauseTime = currentTime;
|
||||
}
|
||||
return false;
|
||||
} else if (mResumed) {
|
||||
mResumed = false;
|
||||
if (mPauseTime > 0) {
|
||||
mDelayStartTime += (currentTime - mPauseTime);
|
||||
}
|
||||
}
|
||||
long deltaTime = currentTime - mDelayStartTime;
|
||||
if (deltaTime > mStartDelay) {
|
||||
mStartTime = currentTime - (deltaTime - mStartDelay);
|
||||
mPlayingState = RUNNING;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean animationFrame(long currentTime) {
|
||||
boolean done = false;
|
||||
switch (mPlayingState) {
|
||||
case RUNNING:
|
||||
case SEEKED:
|
||||
float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
|
||||
if (fraction >= 1f) {
|
||||
if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
|
||||
if (mListeners != null) {
|
||||
int numListeners = mListeners.size();
|
||||
for (AnimatorListener mListener : mListeners) {
|
||||
mListener.onAnimationRepeat(this);
|
||||
}
|
||||
}
|
||||
if (mRepeatMode == REVERSE) {
|
||||
mPlayingBackwards = !mPlayingBackwards;
|
||||
}
|
||||
mCurrentIteration += (int)fraction;
|
||||
fraction = fraction % 1f;
|
||||
mStartTime += mDuration;
|
||||
} else {
|
||||
done = true;
|
||||
fraction = Math.min(fraction, 1.0f);
|
||||
}
|
||||
}
|
||||
if (mPlayingBackwards) {
|
||||
fraction = 1f - fraction;
|
||||
}
|
||||
animateValue(fraction);
|
||||
break;
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
final boolean doAnimationFrame(long frameTime) {
|
||||
if (mPlayingState == STOPPED) {
|
||||
mPlayingState = RUNNING;
|
||||
if (mSeekTime < 0) {
|
||||
mStartTime = frameTime;
|
||||
} else {
|
||||
mStartTime = frameTime - mSeekTime;
|
||||
mSeekTime = -1;
|
||||
}
|
||||
}
|
||||
if (mPaused) {
|
||||
if (mPauseTime < 0) {
|
||||
mPauseTime = frameTime;
|
||||
}
|
||||
return false;
|
||||
} else if (mResumed) {
|
||||
mResumed = false;
|
||||
if (mPauseTime > 0) {
|
||||
mStartTime += (frameTime - mPauseTime);
|
||||
}
|
||||
}
|
||||
final long currentTime = Math.max(frameTime, mStartTime);
|
||||
return animationFrame(currentTime);
|
||||
}
|
||||
|
||||
public float getAnimatedFraction() {
|
||||
return mCurrentFraction;
|
||||
}
|
||||
|
||||
void animateValue(float fraction) {
|
||||
fraction = mInterpolator.getInterpolation(fraction);
|
||||
mCurrentFraction = fraction;
|
||||
int numValues = mValues.length;
|
||||
for (PropertyValuesHolder mValue : mValues) {
|
||||
mValue.calculateValue(fraction);
|
||||
}
|
||||
if (mUpdateListeners != null) {
|
||||
int numListeners = mUpdateListeners.size();
|
||||
for (AnimatorUpdateListener mUpdateListener : mUpdateListeners) {
|
||||
mUpdateListener.onAnimationUpdate(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueAnimator clone() {
|
||||
final ValueAnimator anim = (ValueAnimator) super.clone();
|
||||
if (mUpdateListeners != null) {
|
||||
ArrayList<AnimatorUpdateListener> oldListeners = mUpdateListeners;
|
||||
anim.mUpdateListeners = new ArrayList<AnimatorUpdateListener>();
|
||||
int numListeners = oldListeners.size();
|
||||
for (AnimatorUpdateListener oldListener : oldListeners) {
|
||||
anim.mUpdateListeners.add(oldListener);
|
||||
}
|
||||
}
|
||||
anim.mSeekTime = -1;
|
||||
anim.mPlayingBackwards = false;
|
||||
anim.mCurrentIteration = 0;
|
||||
anim.mInitialized = false;
|
||||
anim.mPlayingState = STOPPED;
|
||||
anim.mStartedDelay = false;
|
||||
PropertyValuesHolder[] oldValues = mValues;
|
||||
if (oldValues != null) {
|
||||
int numValues = oldValues.length;
|
||||
anim.mValues = new PropertyValuesHolder[numValues];
|
||||
anim.mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
|
||||
for (int i = 0; i < numValues; ++i) {
|
||||
PropertyValuesHolder newValuesHolder = oldValues[i].clone();
|
||||
anim.mValues[i] = newValuesHolder;
|
||||
anim.mValuesMap.put(newValuesHolder.getPropertyName(), newValuesHolder);
|
||||
}
|
||||
}
|
||||
return anim;
|
||||
}
|
||||
|
||||
public interface AnimatorUpdateListener {
|
||||
void onAnimationUpdate(ValueAnimator animation);
|
||||
}
|
||||
|
||||
public static int getCurrentAnimationsCount() {
|
||||
AnimationHandler handler = sAnimationHandler.get();
|
||||
return handler != null ? handler.mAnimations.size() : 0;
|
||||
}
|
||||
|
||||
public static void clearAllAnimations() {
|
||||
AnimationHandler handler = sAnimationHandler.get();
|
||||
if (handler != null) {
|
||||
handler.mAnimations.clear();
|
||||
handler.mPendingAnimations.clear();
|
||||
handler.mDelayedAnims.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static AnimationHandler getOrCreateAnimationHandler() {
|
||||
AnimationHandler handler = sAnimationHandler.get();
|
||||
if (handler == null) {
|
||||
handler = new AnimationHandler();
|
||||
sAnimationHandler.set(handler);
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
}
|
|
@ -1,349 +0,0 @@
|
|||
/*
|
||||
Copyright 2012 Jake Wharton
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.Animation;
|
||||
|
||||
import android.graphics.Camera;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.Transformation;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
public class View10 extends Animation {
|
||||
|
||||
public static boolean NEED_PROXY = Build.VERSION.SDK_INT < 11;
|
||||
|
||||
private static final WeakHashMap<View, View10> PROXIES = new WeakHashMap<>();
|
||||
|
||||
public static View10 wrap(View view) {
|
||||
View10 proxy = PROXIES.get(view);
|
||||
Animation animation = view.getAnimation();
|
||||
if (proxy == null || proxy != animation && animation != null) {
|
||||
proxy = new View10(view);
|
||||
PROXIES.put(view, proxy);
|
||||
} else if (animation == null) {
|
||||
view.setAnimation(proxy);
|
||||
}
|
||||
return proxy;
|
||||
}
|
||||
|
||||
private final WeakReference<View> mView;
|
||||
private final Camera mCamera = new Camera();
|
||||
private boolean mHasPivot;
|
||||
|
||||
private float mAlpha = 1;
|
||||
private float mPivotX;
|
||||
private float mPivotY;
|
||||
private float mRotationX;
|
||||
private float mRotationY;
|
||||
private float mRotationZ;
|
||||
private float mScaleX = 1;
|
||||
private float mScaleY = 1;
|
||||
private float mTranslationX;
|
||||
private float mTranslationY;
|
||||
|
||||
private final RectF mBefore = new RectF();
|
||||
private final RectF mAfter = new RectF();
|
||||
private final Matrix mTempMatrix = new Matrix();
|
||||
|
||||
private View10(View view) {
|
||||
setDuration(0);
|
||||
setFillAfter(true);
|
||||
view.setAnimation(this);
|
||||
mView = new WeakReference<>(view);
|
||||
}
|
||||
|
||||
public float getAlpha() {
|
||||
return mAlpha;
|
||||
}
|
||||
|
||||
public void setAlpha(float alpha) {
|
||||
if (mAlpha != alpha) {
|
||||
mAlpha = alpha;
|
||||
View view = mView.get();
|
||||
if (view != null) {
|
||||
view.invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float getPivotX() {
|
||||
return mPivotX;
|
||||
}
|
||||
|
||||
public void setPivotX(float pivotX) {
|
||||
if (!mHasPivot || mPivotX != pivotX) {
|
||||
prepareForUpdate();
|
||||
mHasPivot = true;
|
||||
mPivotX = pivotX;
|
||||
invalidateAfterUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public float getPivotY() {
|
||||
return mPivotY;
|
||||
}
|
||||
|
||||
public void setPivotY(float pivotY) {
|
||||
if (!mHasPivot || mPivotY != pivotY) {
|
||||
prepareForUpdate();
|
||||
mHasPivot = true;
|
||||
mPivotY = pivotY;
|
||||
invalidateAfterUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public float getRotation() {
|
||||
return mRotationZ;
|
||||
}
|
||||
|
||||
public void setRotation(float rotation) {
|
||||
if (mRotationZ != rotation) {
|
||||
prepareForUpdate();
|
||||
mRotationZ = rotation;
|
||||
invalidateAfterUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public float getRotationX() {
|
||||
return mRotationX;
|
||||
}
|
||||
|
||||
public void setRotationX(float rotationX) {
|
||||
if (mRotationX != rotationX) {
|
||||
prepareForUpdate();
|
||||
mRotationX = rotationX;
|
||||
invalidateAfterUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public float getRotationY() {
|
||||
return mRotationY;
|
||||
}
|
||||
|
||||
public void setRotationY(float rotationY) {
|
||||
if (mRotationY != rotationY) {
|
||||
prepareForUpdate();
|
||||
mRotationY = rotationY;
|
||||
invalidateAfterUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public float getScaleX() {
|
||||
return mScaleX;
|
||||
}
|
||||
|
||||
public void setScaleX(float scaleX) {
|
||||
if (mScaleX != scaleX) {
|
||||
prepareForUpdate();
|
||||
mScaleX = scaleX;
|
||||
invalidateAfterUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public float getScaleY() {
|
||||
return mScaleY;
|
||||
}
|
||||
|
||||
public void setScaleY(float scaleY) {
|
||||
if (mScaleY != scaleY) {
|
||||
prepareForUpdate();
|
||||
mScaleY = scaleY;
|
||||
invalidateAfterUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public int getScrollX() {
|
||||
View view = mView.get();
|
||||
if (view == null) {
|
||||
return 0;
|
||||
}
|
||||
return view.getScrollX();
|
||||
}
|
||||
|
||||
public void setScrollX(int value) {
|
||||
View view = mView.get();
|
||||
if (view != null) {
|
||||
view.scrollTo(value, view.getScrollY());
|
||||
}
|
||||
}
|
||||
|
||||
public int getScrollY() {
|
||||
View view = mView.get();
|
||||
if (view == null) {
|
||||
return 0;
|
||||
}
|
||||
return view.getScrollY();
|
||||
}
|
||||
|
||||
public void setScrollY(int value) {
|
||||
View view = mView.get();
|
||||
if (view != null) {
|
||||
view.scrollTo(view.getScrollX(), value);
|
||||
}
|
||||
}
|
||||
|
||||
public float getTranslationX() {
|
||||
return mTranslationX;
|
||||
}
|
||||
|
||||
public void setTranslationX(float translationX) {
|
||||
if (mTranslationX != translationX) {
|
||||
prepareForUpdate();
|
||||
mTranslationX = translationX;
|
||||
invalidateAfterUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public float getTranslationY() {
|
||||
return mTranslationY;
|
||||
}
|
||||
|
||||
public void setTranslationY(float translationY) {
|
||||
if (mTranslationY != translationY) {
|
||||
prepareForUpdate();
|
||||
mTranslationY = translationY;
|
||||
invalidateAfterUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public float getX() {
|
||||
View view = mView.get();
|
||||
if (view == null) {
|
||||
return 0;
|
||||
}
|
||||
return view.getLeft() + mTranslationX;
|
||||
}
|
||||
|
||||
public void setX(float x) {
|
||||
View view = mView.get();
|
||||
if (view != null) {
|
||||
setTranslationX(x - view.getLeft());
|
||||
}
|
||||
}
|
||||
|
||||
public float getY() {
|
||||
View view = mView.get();
|
||||
if (view == null) {
|
||||
return 0;
|
||||
}
|
||||
return view.getTop() + mTranslationY;
|
||||
}
|
||||
|
||||
public void setY(float y) {
|
||||
View view = mView.get();
|
||||
if (view != null) {
|
||||
setTranslationY(y - view.getTop());
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareForUpdate() {
|
||||
View view = mView.get();
|
||||
if (view != null) {
|
||||
computeRect(mBefore, view);
|
||||
}
|
||||
}
|
||||
|
||||
private void invalidateAfterUpdate() {
|
||||
View view = mView.get();
|
||||
if (view == null || view.getParent() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final RectF after = mAfter;
|
||||
computeRect(after, view);
|
||||
after.union(mBefore);
|
||||
|
||||
((View) view.getParent()).invalidate(
|
||||
(int) Math.floor(after.left),
|
||||
(int) Math.floor(after.top),
|
||||
(int) Math.ceil(after.right),
|
||||
(int) Math.ceil(after.bottom));
|
||||
}
|
||||
|
||||
private void computeRect(final RectF r, View view) {
|
||||
final float w = view.getWidth();
|
||||
final float h = view.getHeight();
|
||||
|
||||
r.set(0, 0, w, h);
|
||||
|
||||
final Matrix m = mTempMatrix;
|
||||
m.reset();
|
||||
transformMatrix(m, view);
|
||||
mTempMatrix.mapRect(r);
|
||||
|
||||
r.offset(view.getLeft(), view.getTop());
|
||||
|
||||
if (r.right < r.left) {
|
||||
final float f = r.right;
|
||||
r.right = r.left;
|
||||
r.left = f;
|
||||
}
|
||||
if (r.bottom < r.top) {
|
||||
final float f = r.top;
|
||||
r.top = r.bottom;
|
||||
r.bottom = f;
|
||||
}
|
||||
}
|
||||
|
||||
private void transformMatrix(Matrix m, View view) {
|
||||
final float w = view.getWidth();
|
||||
final float h = view.getHeight();
|
||||
final boolean hasPivot = mHasPivot;
|
||||
final float pX = hasPivot ? mPivotX : w / 2f;
|
||||
final float pY = hasPivot ? mPivotY : h / 2f;
|
||||
|
||||
final float rX = mRotationX;
|
||||
final float rY = mRotationY;
|
||||
final float rZ = mRotationZ;
|
||||
if ((rX != 0) || (rY != 0) || (rZ != 0)) {
|
||||
final Camera camera = mCamera;
|
||||
camera.save();
|
||||
camera.rotateX(rX);
|
||||
camera.rotateY(rY);
|
||||
camera.rotateZ(-rZ);
|
||||
camera.getMatrix(m);
|
||||
camera.restore();
|
||||
m.preTranslate(-pX, -pY);
|
||||
m.postTranslate(pX, pY);
|
||||
}
|
||||
|
||||
final float sX = mScaleX;
|
||||
final float sY = mScaleY;
|
||||
if ((sX != 1.0f) || (sY != 1.0f)) {
|
||||
m.postScale(sX, sY);
|
||||
final float sPX = -(pX / w) * ((sX * w) - w);
|
||||
final float sPY = -(pY / h) * ((sY * h) - h);
|
||||
m.postTranslate(sPX, sPY);
|
||||
}
|
||||
|
||||
m.postTranslate(mTranslationX, mTranslationY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyTransformation(float interpolatedTime, Transformation t) {
|
||||
View view = mView.get();
|
||||
if (view != null) {
|
||||
t.setAlpha(mAlpha);
|
||||
transformMatrix(t.getMatrix(), view);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
* This is the source code of Telegram for Android v. 3.x.x.
|
||||
* It is licensed under GNU GPL v. 2 or later.
|
||||
* You should have received a copy of the license in this archive (see LICENSE).
|
||||
*
|
||||
* Copyright Nikolai Kudashov, 2013-2016.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.AnimationCompat;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
|
||||
import org.telegram.messenger.Animation.Animator10;
|
||||
import org.telegram.messenger.Animation.AnimatorListenerAdapter10;
|
||||
import org.telegram.messenger.Animation.View10;
|
||||
|
||||
public class AnimatorListenerAdapterProxy {
|
||||
protected Object animatorListenerAdapter;
|
||||
|
||||
public AnimatorListenerAdapterProxy() {
|
||||
if (View10.NEED_PROXY) {
|
||||
animatorListenerAdapter = new AnimatorListenerAdapter10() {
|
||||
@Override
|
||||
public void onAnimationCancel(Animator10 animation) {
|
||||
AnimatorListenerAdapterProxy.this.onAnimationCancel(animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator10 animation) {
|
||||
AnimatorListenerAdapterProxy.this.onAnimationEnd(animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator10 animation) {
|
||||
AnimatorListenerAdapterProxy.this.onAnimationRepeat(animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator10 animation) {
|
||||
AnimatorListenerAdapterProxy.this.onAnimationStart(animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationPause(Animator10 animation) {
|
||||
AnimatorListenerAdapterProxy.this.onAnimationPause(animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationResume(Animator10 animation) {
|
||||
AnimatorListenerAdapterProxy.this.onAnimationResume(animation);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
animatorListenerAdapter = new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
AnimatorListenerAdapterProxy.this.onAnimationCancel(animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
AnimatorListenerAdapterProxy.this.onAnimationEnd(animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
AnimatorListenerAdapterProxy.this.onAnimationRepeat(animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
AnimatorListenerAdapterProxy.this.onAnimationStart(animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationPause(Animator animation) {
|
||||
AnimatorListenerAdapterProxy.this.onAnimationPause(animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationResume(Animator animation) {
|
||||
AnimatorListenerAdapterProxy.this.onAnimationResume(animation);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public void onAnimationCancel(Object animation) {
|
||||
|
||||
}
|
||||
|
||||
public void onAnimationEnd(Object animation) {
|
||||
|
||||
}
|
||||
|
||||
public void onAnimationRepeat(Object animation) {
|
||||
|
||||
}
|
||||
|
||||
public void onAnimationStart(Object animation) {
|
||||
|
||||
}
|
||||
|
||||
public void onAnimationPause(Object animation) {
|
||||
|
||||
}
|
||||
|
||||
public void onAnimationResume(Object animation) {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
/*
|
||||
* This is the source code of Telegram for Android v. 3.x.x.
|
||||
* It is licensed under GNU GPL v. 2 or later.
|
||||
* You should have received a copy of the license in this archive (see LICENSE).
|
||||
*
|
||||
* Copyright Nikolai Kudashov, 2013-2016.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.AnimationCompat;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import org.telegram.messenger.Animation.Animator10;
|
||||
import org.telegram.messenger.Animation.AnimatorListenerAdapter10;
|
||||
import org.telegram.messenger.Animation.AnimatorSet10;
|
||||
import org.telegram.messenger.Animation.View10;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class AnimatorSetProxy {
|
||||
|
||||
private Object animatorSet;
|
||||
|
||||
public static <T, U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
|
||||
return copyOfRange(original, 0, newLength, newType);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T, U> T[] copyOfRange(U[] original, int start, int end, Class<? extends T[]> newType) {
|
||||
if (start > end) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
int originalLength = original.length;
|
||||
if (start < 0 || start > originalLength) {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
int resultLength = end - start;
|
||||
int copyLength = Math.min(resultLength, originalLength - start);
|
||||
T[] result = (T[]) Array.newInstance(newType.getComponentType(), resultLength);
|
||||
System.arraycopy(original, start, result, 0, copyLength);
|
||||
return result;
|
||||
}
|
||||
|
||||
public AnimatorSetProxy() {
|
||||
if (View10.NEED_PROXY) {
|
||||
animatorSet = new AnimatorSet10();
|
||||
} else {
|
||||
animatorSet = new AnimatorSet();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void playTogether(Object... items) {
|
||||
if (View10.NEED_PROXY) {
|
||||
Animator10[] animators = copyOf(items, items.length, Animator10[].class);
|
||||
((AnimatorSet10) animatorSet).playTogether(animators);
|
||||
} else {
|
||||
Animator[] animators = copyOf(items, items.length, Animator[].class);
|
||||
((AnimatorSet) animatorSet).playTogether(animators);
|
||||
}
|
||||
}
|
||||
|
||||
public void playTogether(ArrayList<Object> items) {
|
||||
if (View10.NEED_PROXY) {
|
||||
ArrayList<Animator10> animators = new ArrayList<>();
|
||||
for (Object obj : items) {
|
||||
animators.add((Animator10)obj);
|
||||
}
|
||||
((AnimatorSet10) animatorSet).playTogether(animators);
|
||||
} else {
|
||||
ArrayList<Animator> animators = new ArrayList<>();
|
||||
for (Object obj : items) {
|
||||
animators.add((Animator)obj);
|
||||
}
|
||||
((AnimatorSet) animatorSet).playTogether(animators);
|
||||
}
|
||||
}
|
||||
|
||||
public AnimatorSetProxy setDuration(long duration) {
|
||||
if (View10.NEED_PROXY) {
|
||||
((AnimatorSet10) animatorSet).setDuration(duration);
|
||||
} else {
|
||||
((AnimatorSet) animatorSet).setDuration(duration);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnimatorSetProxy setStartDelay(long delay) {
|
||||
if (View10.NEED_PROXY) {
|
||||
((AnimatorSet10) animatorSet).setStartDelay(delay);
|
||||
} else {
|
||||
((AnimatorSet) animatorSet).setStartDelay(delay);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (View10.NEED_PROXY) {
|
||||
((AnimatorSet10) animatorSet).start();
|
||||
} else {
|
||||
((AnimatorSet) animatorSet).start();
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
if (View10.NEED_PROXY) {
|
||||
((AnimatorSet10) animatorSet).cancel();
|
||||
} else {
|
||||
((AnimatorSet) animatorSet).cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(AnimatorListenerAdapterProxy listener) {
|
||||
if (View10.NEED_PROXY) {
|
||||
((AnimatorSet10) animatorSet).addListener((AnimatorListenerAdapter10) listener.animatorListenerAdapter);
|
||||
} else {
|
||||
((AnimatorSet) animatorSet).addListener((AnimatorListenerAdapter) listener.animatorListenerAdapter);
|
||||
}
|
||||
}
|
||||
|
||||
public void setInterpolator(Interpolator interpolator) {
|
||||
if (View10.NEED_PROXY) {
|
||||
((AnimatorSet10) animatorSet).setInterpolator(interpolator);
|
||||
} else {
|
||||
((AnimatorSet) animatorSet).setInterpolator(interpolator);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return animatorSet == o;
|
||||
}
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
/*
|
||||
* This is the source code of Telegram for Android v. 3.x.x.
|
||||
* It is licensed under GNU GPL v. 2 or later.
|
||||
* You should have received a copy of the license in this archive (see LICENSE).
|
||||
*
|
||||
* Copyright Nikolai Kudashov, 2013-2016.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.AnimationCompat;
|
||||
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import org.telegram.messenger.Animation.AnimatorListenerAdapter10;
|
||||
import org.telegram.messenger.Animation.ObjectAnimator10;
|
||||
import org.telegram.messenger.Animation.View10;
|
||||
|
||||
public class ObjectAnimatorProxy {
|
||||
|
||||
private Object objectAnimator;
|
||||
|
||||
public ObjectAnimatorProxy(Object animator) {
|
||||
objectAnimator = animator;
|
||||
}
|
||||
|
||||
public static Object ofFloat(Object target, String propertyName, float... values) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return ObjectAnimator10.ofFloat(target, propertyName, values);
|
||||
} else {
|
||||
return ObjectAnimator.ofFloat(target, propertyName, values);
|
||||
}
|
||||
}
|
||||
|
||||
public static Object ofInt(Object target, String propertyName, int... values) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return ObjectAnimator10.ofInt(target, propertyName, values);
|
||||
} else {
|
||||
return ObjectAnimator.ofInt(target, propertyName, values);
|
||||
}
|
||||
}
|
||||
|
||||
public static ObjectAnimatorProxy ofFloatProxy(Object target, String propertyName, float... values) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return new ObjectAnimatorProxy(ObjectAnimator10.ofFloat(target, propertyName, values));
|
||||
} else {
|
||||
return new ObjectAnimatorProxy(ObjectAnimator.ofFloat(target, propertyName, values));
|
||||
}
|
||||
}
|
||||
|
||||
public static ObjectAnimatorProxy ofIntProxy(Object target, String propertyName, int... values) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return new ObjectAnimatorProxy(ObjectAnimator10.ofInt(target, propertyName, values));
|
||||
} else {
|
||||
return new ObjectAnimatorProxy(ObjectAnimator.ofInt(target, propertyName, values));
|
||||
}
|
||||
}
|
||||
|
||||
public ObjectAnimatorProxy setDuration(long duration) {
|
||||
if (View10.NEED_PROXY) {
|
||||
((ObjectAnimator10) objectAnimator).setDuration(duration);
|
||||
} else {
|
||||
((ObjectAnimator) objectAnimator).setDuration(duration);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setInterpolator(Interpolator value) {
|
||||
if (View10.NEED_PROXY) {
|
||||
((ObjectAnimator10) objectAnimator).setInterpolator(value);
|
||||
} else {
|
||||
((ObjectAnimator) objectAnimator).setInterpolator(value);
|
||||
}
|
||||
}
|
||||
|
||||
public ObjectAnimatorProxy start() {
|
||||
if (View10.NEED_PROXY) {
|
||||
((ObjectAnimator10) objectAnimator).start();
|
||||
} else {
|
||||
((ObjectAnimator) objectAnimator).start();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setAutoCancel(boolean cancel) {
|
||||
if (View10.NEED_PROXY) {
|
||||
((ObjectAnimator10) objectAnimator).setAutoCancel(cancel);
|
||||
} else {
|
||||
((ObjectAnimator) objectAnimator).setAutoCancel(cancel);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
if (View10.NEED_PROXY) {
|
||||
return ((ObjectAnimator10) objectAnimator).isRunning();
|
||||
} else {
|
||||
return ((ObjectAnimator) objectAnimator).isRunning();
|
||||
}
|
||||
}
|
||||
|
||||
public void end() {
|
||||
if (View10.NEED_PROXY) {
|
||||
((ObjectAnimator10) objectAnimator).end();
|
||||
} else {
|
||||
((ObjectAnimator) objectAnimator).end();
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
if (View10.NEED_PROXY) {
|
||||
((ObjectAnimator10) objectAnimator).cancel();
|
||||
} else {
|
||||
((ObjectAnimator) objectAnimator).cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public ObjectAnimatorProxy addListener(AnimatorListenerAdapterProxy listener) {
|
||||
if (View10.NEED_PROXY) {
|
||||
((ObjectAnimator10) objectAnimator).addListener((AnimatorListenerAdapter10) listener.animatorListenerAdapter);
|
||||
} else {
|
||||
((ObjectAnimator) objectAnimator).addListener((AnimatorListenerAdapter) listener.animatorListenerAdapter);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return objectAnimator == o;
|
||||
}
|
||||
}
|
|
@ -1,248 +0,0 @@
|
|||
/*
|
||||
* This is the source code of Telegram for Android v. 3.x.x.
|
||||
* It is licensed under GNU GPL v. 2 or later.
|
||||
* You should have received a copy of the license in this archive (see LICENSE).
|
||||
*
|
||||
* Copyright Nikolai Kudashov, 2013-2016.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger.AnimationCompat;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import org.telegram.messenger.Animation.View10;
|
||||
|
||||
public class ViewProxy {
|
||||
|
||||
public static float getAlpha(View view) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return View10.wrap(view).getAlpha();
|
||||
} else {
|
||||
return view.getAlpha();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setAlpha(View view, float alpha) {
|
||||
if (View10.NEED_PROXY) {
|
||||
View10.wrap(view).setAlpha(alpha);
|
||||
} else {
|
||||
view.setAlpha(alpha);
|
||||
}
|
||||
}
|
||||
|
||||
public static float getPivotX(View view) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return View10.wrap(view).getPivotX();
|
||||
} else {
|
||||
return view.getPivotX();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setPivotX(View view, float pivotX) {
|
||||
if (View10.NEED_PROXY) {
|
||||
View10.wrap(view).setPivotX(pivotX);
|
||||
} else {
|
||||
view.setPivotX(pivotX);
|
||||
}
|
||||
}
|
||||
|
||||
public static float getPivotY(View view) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return View10.wrap(view).getPivotY();
|
||||
} else {
|
||||
return view.getPivotY();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setPivotY(View view, float pivotY) {
|
||||
if (View10.NEED_PROXY) {
|
||||
View10.wrap(view).setPivotY(pivotY);
|
||||
} else {
|
||||
view.setPivotY(pivotY);
|
||||
}
|
||||
}
|
||||
|
||||
public static float getRotation(View view) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return View10.wrap(view).getRotation();
|
||||
} else {
|
||||
return view.getRotation();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setRotation(View view, float rotation) {
|
||||
if (View10.NEED_PROXY) {
|
||||
View10.wrap(view).setRotation(rotation);
|
||||
} else {
|
||||
view.setRotation(rotation);
|
||||
}
|
||||
}
|
||||
|
||||
public static float getRotationX(View view) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return View10.wrap(view).getRotationX();
|
||||
} else {
|
||||
return view.getRotationX();
|
||||
}
|
||||
}
|
||||
|
||||
public void setRotationX(View view, float rotationX) {
|
||||
if (View10.NEED_PROXY) {
|
||||
View10.wrap(view).setRotationX(rotationX);
|
||||
} else {
|
||||
view.setRotationX(rotationX);
|
||||
}
|
||||
}
|
||||
|
||||
public static float getRotationY(View view) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return View10.wrap(view).getRotationY();
|
||||
} else {
|
||||
return view.getRotationY();
|
||||
}
|
||||
}
|
||||
|
||||
public void setRotationY(View view, float rotationY) {
|
||||
if (View10.NEED_PROXY) {
|
||||
View10.wrap(view).setRotationY(rotationY);
|
||||
} else {
|
||||
view.setRotationY(rotationY);
|
||||
}
|
||||
}
|
||||
|
||||
public static float getScaleX(View view) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return View10.wrap(view).getScaleX();
|
||||
} else {
|
||||
return view.getScaleX();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setScaleX(View view, float scaleX) {
|
||||
if (View10.NEED_PROXY) {
|
||||
View10.wrap(view).setScaleX(scaleX);
|
||||
} else {
|
||||
view.setScaleX(scaleX);
|
||||
}
|
||||
}
|
||||
|
||||
public static float getScaleY(View view) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return View10.wrap(view).getScaleY();
|
||||
} else {
|
||||
return view.getScaleY();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setScaleY(View view, float scaleY) {
|
||||
if (View10.NEED_PROXY) {
|
||||
View10.wrap(view).setScaleY(scaleY);
|
||||
} else {
|
||||
view.setScaleY(scaleY);
|
||||
}
|
||||
}
|
||||
|
||||
public static int getScrollX(View view) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return View10.wrap(view).getScrollX();
|
||||
} else {
|
||||
return view.getScrollX();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setScrollX(View view, int value) {
|
||||
if (View10.NEED_PROXY) {
|
||||
View10.wrap(view).setScrollX(value);
|
||||
} else {
|
||||
view.setScrollX(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static int getScrollY(View view) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return View10.wrap(view).getScrollY();
|
||||
} else {
|
||||
return view.getScrollY();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setScrollY(View view, int value) {
|
||||
if (View10.NEED_PROXY) {
|
||||
View10.wrap(view).setScrollY(value);
|
||||
} else {
|
||||
view.setScrollY(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static float getTranslationX(View view) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return View10.wrap(view).getTranslationX();
|
||||
} else {
|
||||
return view.getTranslationX();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setTranslationX(View view, float translationX) {
|
||||
if (View10.NEED_PROXY) {
|
||||
View10.wrap(view).setTranslationX(translationX);
|
||||
} else {
|
||||
view.setTranslationX(translationX);
|
||||
}
|
||||
}
|
||||
|
||||
public static float getTranslationY(View view) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return View10.wrap(view).getTranslationY();
|
||||
} else {
|
||||
return view.getTranslationY();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setTranslationY(View view, float translationY) {
|
||||
if (View10.NEED_PROXY) {
|
||||
View10.wrap(view).setTranslationY(translationY);
|
||||
} else {
|
||||
view.setTranslationY(translationY);
|
||||
}
|
||||
}
|
||||
|
||||
public static float getX(View view) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return View10.wrap(view).getX();
|
||||
} else {
|
||||
return view.getX();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setX(View view, float x) {
|
||||
if (View10.NEED_PROXY) {
|
||||
View10.wrap(view).setX(x);
|
||||
} else {
|
||||
view.setX(x);
|
||||
}
|
||||
}
|
||||
|
||||
public static float getY(View view) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return View10.wrap(view).getY();
|
||||
} else {
|
||||
return view.getY();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setY(View view, float y) {
|
||||
if (View10.NEED_PROXY) {
|
||||
View10.wrap(view).setY(y);
|
||||
} else {
|
||||
view.setY(y);
|
||||
}
|
||||
}
|
||||
|
||||
public static Object wrap(View view) {
|
||||
if (View10.NEED_PROXY) {
|
||||
return View10.wrap(view);
|
||||
} else {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* This is the source code of Telegram for Android v. 3.x.x.
|
||||
* It is licensed under GNU GPL v. 2 or later.
|
||||
* You should have received a copy of the license in this archive (see LICENSE).
|
||||
*
|
||||
* Copyright Nikolai Kudashov, 2013-2016.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
|
||||
public class AnimatorListenerAdapterProxy extends AnimatorListenerAdapter {
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animator) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animator) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animator) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationPause(Animator animator) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationResume(Animator animator) {
|
||||
|
||||
}
|
||||
}
|
|
@ -287,40 +287,40 @@ public class ApplicationLoader extends Application {
|
|||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
if (Build.VERSION.SDK_INT < 11) {
|
||||
java.lang.System.setProperty("java.net.preferIPv4Stack", "true");
|
||||
java.lang.System.setProperty("java.net.preferIPv6Addresses", "false");
|
||||
}
|
||||
|
||||
applicationContext = getApplicationContext();
|
||||
NativeLoader.initNativeLibs(ApplicationLoader.applicationContext);
|
||||
ConnectionsManager.native_setJava(Build.VERSION.SDK_INT == 14 || Build.VERSION.SDK_INT == 15);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 14) {
|
||||
new ForegroundDetector(this);
|
||||
}
|
||||
new ForegroundDetector(this);
|
||||
|
||||
applicationHandler = new Handler(applicationContext.getMainLooper());
|
||||
|
||||
startPushService();
|
||||
}
|
||||
|
||||
/*public static void sendRegIdToBackend(final String token) {
|
||||
Utilities.stageQueue.postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
UserConfig.pushString = token;
|
||||
UserConfig.registeredForPush = false;
|
||||
UserConfig.saveConfig(false);
|
||||
if (UserConfig.getClientUserId() != 0) {
|
||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
MessagesController.getInstance().registerForPush(token);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}*/
|
||||
|
||||
public static void startPushService() {
|
||||
SharedPreferences preferences = applicationContext.getSharedPreferences("Notifications", MODE_PRIVATE);
|
||||
|
||||
if (preferences.getBoolean("pushService", true)) {
|
||||
applicationContext.startService(new Intent(applicationContext, NotificationsService.class));
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= 19) {
|
||||
// Calendar cal = Calendar.getInstance();
|
||||
// PendingIntent pintent = PendingIntent.getService(applicationContext, 0, new Intent(applicationContext, NotificationsService.class), 0);
|
||||
// AlarmManager alarm = (AlarmManager) applicationContext.getSystemService(Context.ALARM_SERVICE);
|
||||
// alarm.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 30000, pintent);
|
||||
|
||||
PendingIntent pintent = PendingIntent.getService(applicationContext, 0, new Intent(applicationContext, NotificationsService.class), 0);
|
||||
AlarmManager alarm = (AlarmManager)applicationContext.getSystemService(Context.ALARM_SERVICE);
|
||||
alarm.cancel(pintent);
|
||||
}
|
||||
} else {
|
||||
stopPushService();
|
||||
}
|
||||
|
@ -357,8 +357,8 @@ public class ApplicationLoader extends Application {
|
|||
}
|
||||
|
||||
//if (UserConfig.pushString == null || UserConfig.pushString.length() == 0) {
|
||||
Intent intent = new Intent(applicationContext, GcmRegistrationIntentService.class);
|
||||
startService(intent);
|
||||
Intent intent = new Intent(applicationContext, GcmRegistrationIntentService.class);
|
||||
startService(intent);
|
||||
//} else {
|
||||
// FileLog.d("tmessages", "GCM regId = " + UserConfig.pushString);
|
||||
//}
|
||||
|
@ -369,6 +369,33 @@ public class ApplicationLoader extends Application {
|
|||
}, 1000);
|
||||
}
|
||||
|
||||
/*private void initPlayServices() {
|
||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (checkPlayServices()) {
|
||||
if (UserConfig.pushString != null && UserConfig.pushString.length() != 0) {
|
||||
FileLog.d("tmessages", "GCM regId = " + UserConfig.pushString);
|
||||
} else {
|
||||
FileLog.d("tmessages", "GCM Registration not found.");
|
||||
}
|
||||
try {
|
||||
if (!FirebaseApp.getApps(ApplicationLoader.applicationContext).isEmpty()) {
|
||||
String token = FirebaseInstanceId.getInstance().getToken();
|
||||
if (token != null) {
|
||||
sendRegIdToBackend(token);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
} else {
|
||||
FileLog.d("tmessages", "No valid Google Play Services APK found.");
|
||||
}
|
||||
}
|
||||
}, 2000);
|
||||
}*/
|
||||
|
||||
private boolean checkPlayServices() {
|
||||
try {
|
||||
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
|
||||
|
|
|
@ -32,7 +32,7 @@ public class AutoMessageReplyReceiver extends BroadcastReceiver {
|
|||
if (dialog_id == 0 || max_id == 0) {
|
||||
return;
|
||||
}
|
||||
SendMessagesHelper.getInstance().sendMessage(text.toString(), dialog_id, null, null, true, false, null, null, null);
|
||||
SendMessagesHelper.getInstance().sendMessage(text.toString(), dialog_id, null, null, true, null, null, null);
|
||||
MessagesController.getInstance().markDialogAsRead(dialog_id, max_id, max_id, 0, true, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ public class Bitmaps {
|
|||
|
||||
public static Bitmap createBitmap(int width, int height, Bitmap.Config config) {
|
||||
Bitmap bitmap;
|
||||
if (Build.VERSION.SDK_INT >= 14 && Build.VERSION.SDK_INT < 21) {
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inDither = true;
|
||||
options.inPreferredConfig = config;
|
||||
|
@ -212,9 +212,7 @@ public class Bitmaps {
|
|||
}
|
||||
}
|
||||
bitmap.setDensity(source.getDensity());
|
||||
if (Build.VERSION.SDK_INT >= 12) {
|
||||
bitmap.setHasAlpha(source.hasAlpha());
|
||||
}
|
||||
bitmap.setHasAlpha(source.hasAlpha());
|
||||
if (Build.VERSION.SDK_INT >= 19) {
|
||||
bitmap.setPremultiplied(source.isPremultiplied());
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ package org.telegram.messenger;
|
|||
|
||||
public class BuildVars {
|
||||
public static boolean DEBUG_VERSION = false;
|
||||
public static int BUILD_VERSION = 803;
|
||||
public static String BUILD_VERSION_STRING = "3.9";
|
||||
public static int BUILD_VERSION = 821;
|
||||
public static String BUILD_VERSION_STRING = "3.10";
|
||||
public static int APP_ID = 0; //obtain your own APP_ID at https://core.telegram.org/api/obtaining_api_id
|
||||
public static String APP_HASH = ""; //obtain your own APP_HASH at https://core.telegram.org/api/obtaining_api_id
|
||||
public static String HOCKEY_APP_HASH = "your-hockeyapp-api-key-here";
|
||||
|
|
|
@ -71,17 +71,17 @@ public class ContactsController {
|
|||
}
|
||||
|
||||
private String[] projectionPhones = {
|
||||
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
|
||||
ContactsContract.CommonDataKinds.Phone.NUMBER,
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE,
|
||||
ContactsContract.CommonDataKinds.Phone.LABEL
|
||||
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
|
||||
ContactsContract.CommonDataKinds.Phone.NUMBER,
|
||||
ContactsContract.CommonDataKinds.Phone.TYPE,
|
||||
ContactsContract.CommonDataKinds.Phone.LABEL
|
||||
};
|
||||
private String[] projectionNames = {
|
||||
ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID,
|
||||
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
|
||||
ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,
|
||||
ContactsContract.Data.DISPLAY_NAME,
|
||||
ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME
|
||||
ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID,
|
||||
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
|
||||
ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,
|
||||
ContactsContract.Data.DISPLAY_NAME,
|
||||
ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME
|
||||
};
|
||||
|
||||
public HashMap<Integer, Contact> contactsBook = new HashMap<>();
|
||||
|
@ -98,7 +98,10 @@ public class ContactsController {
|
|||
|
||||
public HashMap<String, TLRPC.TL_contact> contactsByPhone = new HashMap<>();
|
||||
|
||||
private int completedRequestsCount;
|
||||
|
||||
private static volatile ContactsController Instance = null;
|
||||
|
||||
public static ContactsController getInstance() {
|
||||
ContactsController localInstance = Instance;
|
||||
if (localInstance == null) {
|
||||
|
@ -163,6 +166,12 @@ public class ContactsController {
|
|||
deleteAccountTTL = 0;
|
||||
loadingLastSeenInfo = 0;
|
||||
loadingGroupInfo = 0;
|
||||
Utilities.globalQueue.postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
completedRequestsCount = 0;
|
||||
}
|
||||
});
|
||||
privacyRules = null;
|
||||
}
|
||||
|
||||
|
@ -170,13 +179,9 @@ public class ContactsController {
|
|||
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
|
||||
inviteText = preferences.getString("invitetext", null);
|
||||
int time = preferences.getInt("invitetexttime", 0);
|
||||
if (!updatingInviteText && (inviteText == null || time + 86400 < (int)(System.currentTimeMillis() / 1000))) {
|
||||
if (!updatingInviteText && (inviteText == null || time + 86400 < (int) (System.currentTimeMillis() / 1000))) {
|
||||
updatingInviteText = true;
|
||||
TLRPC.TL_help_getInviteText req = new TLRPC.TL_help_getInviteText();
|
||||
req.lang_code = LocaleController.getLocaleStringIso639();
|
||||
if (req.lang_code.length() == 0) {
|
||||
req.lang_code = "en";
|
||||
}
|
||||
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
|
||||
@Override
|
||||
public void run(TLObject response, TLRPC.TL_error error) {
|
||||
|
@ -275,12 +280,21 @@ public class ContactsController {
|
|||
public void run() {
|
||||
if (checkContactsInternal()) {
|
||||
FileLog.e("tmessages", "detected contacts change");
|
||||
ContactsController.getInstance().performSyncPhoneBook(ContactsController.getInstance().getContactsCopy(ContactsController.getInstance().contactsBook), true, false, true);
|
||||
ContactsController.getInstance().performSyncPhoneBook(ContactsController.getInstance().getContactsCopy(ContactsController.getInstance().contactsBook), true, false, true, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void forceImportContacts() {
|
||||
Utilities.globalQueue.postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ContactsController.getInstance().performSyncPhoneBook(new HashMap<Integer, Contact>(), true, true, true, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean checkContactsInternal() {
|
||||
boolean reload = false;
|
||||
try {
|
||||
|
@ -444,7 +458,7 @@ public class ContactsController {
|
|||
}
|
||||
|
||||
try {
|
||||
pCur = cr.query(ContactsContract.RawContacts.CONTENT_URI, new String[] { "display_name", ContactsContract.RawContacts.SYNC1, ContactsContract.RawContacts.CONTACT_ID }, ContactsContract.RawContacts.ACCOUNT_TYPE + " = " + "'com.whatsapp'", null, null);
|
||||
pCur = cr.query(ContactsContract.RawContacts.CONTENT_URI, new String[]{"display_name", ContactsContract.RawContacts.SYNC1, ContactsContract.RawContacts.CONTACT_ID}, ContactsContract.RawContacts.ACCOUNT_TYPE + " = " + "'com.whatsapp'", null, null);
|
||||
if (pCur != null) {
|
||||
while ((pCur.moveToNext())) {
|
||||
String phone = pCur.getString(1);
|
||||
|
@ -466,8 +480,8 @@ public class ContactsController {
|
|||
}
|
||||
|
||||
String name = pCur.getString(0);
|
||||
if (name == null || name.length() == 0) {
|
||||
name = PhoneFormat.getInstance().format(phone);
|
||||
if (TextUtils.isEmpty(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Contact contact = new Contact();
|
||||
|
@ -528,7 +542,7 @@ public class ContactsController {
|
|||
return ret;
|
||||
}
|
||||
|
||||
public void performSyncPhoneBook(final HashMap<Integer, Contact> contactHashMap, final boolean requ, final boolean first, final boolean schedule) {
|
||||
protected void performSyncPhoneBook(final HashMap<Integer, Contact> contactHashMap, final boolean request, final boolean first, final boolean schedule, final boolean force) {
|
||||
if (!first && !contactsBookLoaded) {
|
||||
return;
|
||||
}
|
||||
|
@ -560,21 +574,11 @@ public class ContactsController {
|
|||
}
|
||||
}*/
|
||||
|
||||
boolean request = requ;
|
||||
if (request && first) {
|
||||
if (UserConfig.importHash != null && UserConfig.importHash.length() != 0 || UserConfig.contactsVersion != 1) {
|
||||
UserConfig.importHash = "";
|
||||
UserConfig.contactsVersion = 1;
|
||||
UserConfig.saveConfig(false);
|
||||
request = false;
|
||||
}
|
||||
}
|
||||
|
||||
HashMap<String, Contact> contactShortHashMap = new HashMap<>();
|
||||
for (HashMap.Entry<Integer, Contact> entry : contactHashMap.entrySet()) {
|
||||
Contact c = entry.getValue();
|
||||
for (String sphone : c.shortPhones) {
|
||||
contactShortHashMap.put(sphone, c);
|
||||
for (int a = 0; a < c.shortPhones.size(); a++) {
|
||||
contactShortHashMap.put(c.shortPhones.get(a), c);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -593,8 +597,8 @@ public class ContactsController {
|
|||
Contact value = pair.getValue();
|
||||
Contact existing = contactHashMap.get(id);
|
||||
if (existing == null) {
|
||||
for (String s : value.shortPhones) {
|
||||
Contact c = contactShortHashMap.get(s);
|
||||
for (int a = 0; a < value.shortPhones.size(); a++) {
|
||||
Contact c = contactShortHashMap.get(value.shortPhones.get(a));
|
||||
if (c != null) {
|
||||
existing = c;
|
||||
id = existing.id;
|
||||
|
@ -603,7 +607,7 @@ public class ContactsController {
|
|||
}
|
||||
}
|
||||
|
||||
boolean nameChanged = existing != null && (value.first_name != null && value.first_name.length() != 0 && !existing.first_name.equals(value.first_name) || value.last_name != null && existing.last_name != null && !existing.last_name.equals(value.last_name));
|
||||
boolean nameChanged = existing != null && (TextUtils.isEmpty(value.first_name) && !existing.first_name.equals(value.first_name) || !TextUtils.isEmpty(value.last_name) && !existing.last_name.equals(value.last_name));
|
||||
if (existing == null || nameChanged) {
|
||||
for (int a = 0; a < value.phones.size(); a++) {
|
||||
String sphone = value.shortPhones.get(a);
|
||||
|
@ -625,6 +629,7 @@ public class ContactsController {
|
|||
|
||||
TLRPC.TL_inputPhoneContact imp = new TLRPC.TL_inputPhoneContact();
|
||||
imp.client_id = value.id;
|
||||
imp.client_id |= ((long) a) << 32;
|
||||
imp.first_name = value.first_name;
|
||||
imp.last_name = value.last_name;
|
||||
imp.phone = value.phones.get(a);
|
||||
|
@ -644,13 +649,14 @@ public class ContactsController {
|
|||
TLRPC.TL_contact contact = contactsByPhone.get(sphone);
|
||||
if (contact != null) {
|
||||
TLRPC.User user = MessagesController.getInstance().getUser(contact.user_id);
|
||||
if (user == null || user.first_name != null && user.first_name.length() != 0 || user.last_name != null && user.last_name.length() != 0) {
|
||||
if (user == null || !TextUtils.isEmpty(user.first_name) || !TextUtils.isEmpty(user.last_name) || TextUtils.isEmpty(value.first_name) && TextUtils.isEmpty(value.last_name)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
TLRPC.TL_inputPhoneContact imp = new TLRPC.TL_inputPhoneContact();
|
||||
imp.client_id = value.id;
|
||||
imp.client_id |= ((long) a) << 32;
|
||||
imp.first_name = value.first_name;
|
||||
imp.last_name = value.last_name;
|
||||
imp.phone = value.phones.get(a);
|
||||
|
@ -697,7 +703,8 @@ public class ContactsController {
|
|||
try {
|
||||
final HashMap<String, TLRPC.User> contactsPhonesShort = new HashMap<>();
|
||||
|
||||
for (TLRPC.TL_contact value : contacts) {
|
||||
for (int a = 0; a < contacts.size(); a++) {
|
||||
TLRPC.TL_contact value = contacts.get(a);
|
||||
TLRPC.User user = MessagesController.getInstance().getUser(value.user_id);
|
||||
if (user == null || user.phone == null || user.phone.length() == 0) {
|
||||
continue;
|
||||
|
@ -739,16 +746,19 @@ public class ContactsController {
|
|||
Contact value = pair.getValue();
|
||||
int id = pair.getKey();
|
||||
for (int a = 0; a < value.phones.size(); a++) {
|
||||
String phone = value.shortPhones.get(a);
|
||||
TLRPC.TL_contact contact = contactsByPhone.get(phone);
|
||||
if (contact != null) {
|
||||
TLRPC.User user = MessagesController.getInstance().getUser(contact.user_id);
|
||||
if (user == null || user.first_name != null && user.first_name.length() != 0 || user.last_name != null && user.last_name.length() != 0) {
|
||||
continue;
|
||||
if (!force) {
|
||||
String phone = value.shortPhones.get(a);
|
||||
TLRPC.TL_contact contact = contactsByPhone.get(phone);
|
||||
if (contact != null) {
|
||||
TLRPC.User user = MessagesController.getInstance().getUser(contact.user_id);
|
||||
if (user == null || !TextUtils.isEmpty(user.first_name) || !TextUtils.isEmpty(user.last_name) || TextUtils.isEmpty(value.first_name) && TextUtils.isEmpty(value.last_name)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
TLRPC.TL_inputPhoneContact imp = new TLRPC.TL_inputPhoneContact();
|
||||
imp.client_id = id;
|
||||
imp.client_id |= ((long) a) << 32;
|
||||
imp.first_name = value.first_name;
|
||||
imp.last_name = value.last_name;
|
||||
imp.phone = value.phones.get(a);
|
||||
|
@ -767,23 +777,34 @@ public class ContactsController {
|
|||
FileLog.e("tmessages", "add contact " + contact.first_name + " " + contact.last_name + " " + contact.phone);
|
||||
}
|
||||
}*/
|
||||
final int count = (int)Math.ceil(toImport.size() / 500.0f);
|
||||
|
||||
final HashMap<Integer, Contact> contactsMapToSave = new HashMap<>(contactsMap);
|
||||
completedRequestsCount = 0;
|
||||
final int count = (int) Math.ceil(toImport.size() / 500.0f);
|
||||
for (int a = 0; a < count; a++) {
|
||||
ArrayList<TLRPC.TL_inputPhoneContact> finalToImport = new ArrayList<>();
|
||||
finalToImport.addAll(toImport.subList(a * 500, Math.min((a + 1) * 500, toImport.size())));
|
||||
TLRPC.TL_contacts_importContacts req = new TLRPC.TL_contacts_importContacts();
|
||||
req.contacts = finalToImport;
|
||||
req.replace = false;
|
||||
final boolean isLastQuery = a == count - 1;
|
||||
ConnectionsManager.getInstance().sendRequest(req, new RequestDelegate() {
|
||||
@Override
|
||||
public void run(TLObject response, TLRPC.TL_error error) {
|
||||
completedRequestsCount++;
|
||||
if (error == null) {
|
||||
FileLog.e("tmessages", "contacts imported");
|
||||
if (isLastQuery && !contactsMap.isEmpty()) {
|
||||
MessagesStorage.getInstance().putCachedPhoneBook(contactsMap);
|
||||
TLRPC.TL_contacts_importedContacts res = (TLRPC.TL_contacts_importedContacts) response;
|
||||
if (!res.retry_contacts.isEmpty()) {
|
||||
for (int a = 0; a < res.retry_contacts.size(); a++) {
|
||||
long id = res.retry_contacts.get(a);
|
||||
contactsMapToSave.remove((int) id);
|
||||
}
|
||||
}
|
||||
TLRPC.TL_contacts_importedContacts res = (TLRPC.TL_contacts_importedContacts)response;
|
||||
|
||||
if (completedRequestsCount == count && !contactsMapToSave.isEmpty()) {
|
||||
MessagesStorage.getInstance().putCachedPhoneBook(contactsMapToSave);
|
||||
}
|
||||
|
||||
/*if (BuildVars.DEBUG_VERSION) {
|
||||
for (TLRPC.User user : res.users) {
|
||||
FileLog.e("tmessages", "received user " + user.first_name + " " + user.last_name + " " + user.phone);
|
||||
|
@ -791,16 +812,16 @@ public class ContactsController {
|
|||
}*/
|
||||
MessagesStorage.getInstance().putUsersAndChats(res.users, null, true, true);
|
||||
ArrayList<TLRPC.TL_contact> cArr = new ArrayList<>();
|
||||
for (TLRPC.TL_importedContact c : res.imported) {
|
||||
for (int a = 0; a < res.imported.size(); a++) {
|
||||
TLRPC.TL_contact contact = new TLRPC.TL_contact();
|
||||
contact.user_id = c.user_id;
|
||||
contact.user_id = res.imported.get(a).user_id;
|
||||
cArr.add(contact);
|
||||
}
|
||||
processLoadedContacts(cArr, res.users, 2);
|
||||
} else {
|
||||
FileLog.e("tmessages", "import contacts error " + error.text);
|
||||
}
|
||||
if (isLastQuery) {
|
||||
if (completedRequestsCount == count) {
|
||||
Utilities.stageQueue.postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -836,13 +857,13 @@ public class ContactsController {
|
|||
applyContactsUpdates(delayedContactsUpdate, null, null, null);
|
||||
delayedContactsUpdate.clear();
|
||||
}
|
||||
}
|
||||
});
|
||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateUnregisteredContacts(contacts);
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.contactsDidLoaded);
|
||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateUnregisteredContacts(contacts);
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.contactsDidLoaded);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -892,7 +913,7 @@ public class ContactsController {
|
|||
@Override
|
||||
public void run(TLObject response, TLRPC.TL_error error) {
|
||||
if (error == null) {
|
||||
TLRPC.contacts_Contacts res = (TLRPC.contacts_Contacts)response;
|
||||
TLRPC.contacts_Contacts res = (TLRPC.contacts_Contacts) response;
|
||||
if (res instanceof TLRPC.TL_contacts_contactsNotModified) {
|
||||
contactsLoaded = true;
|
||||
if (!delayedContactsUpdate.isEmpty() && contactsBookLoaded) {
|
||||
|
@ -940,11 +961,10 @@ public class ContactsController {
|
|||
contactsArr.addAll(contacts);
|
||||
}
|
||||
|
||||
for (TLRPC.TL_contact contact : contactsArr) {
|
||||
TLRPC.User user = MessagesController.getInstance().getUser(contact.user_id);
|
||||
for (int a = 0; a < contactsArr.size(); a++) {
|
||||
TLRPC.User user = MessagesController.getInstance().getUser(contactsArr.get(a).user_id);
|
||||
if (user != null) {
|
||||
usersDict.put(user.id, user);
|
||||
|
||||
//if (BuildVars.DEBUG_VERSION) {
|
||||
// FileLog.e("tmessages", "loaded user contact " + user.first_name + " " + user.last_name + " " + user.phone);
|
||||
//}
|
||||
|
@ -955,7 +975,7 @@ public class ContactsController {
|
|||
@Override
|
||||
public void run() {
|
||||
FileLog.e("tmessages", "done loading contacts");
|
||||
if (from == 1 && (contactsArr.isEmpty() || UserConfig.lastContactsSyncTime < (int) (System.currentTimeMillis() / 1000) - 24 * 60 * 60)) {
|
||||
if (from == 1 && (contactsArr.isEmpty() || Math.abs(System.currentTimeMillis() / 1000 - UserConfig.lastContactsSyncTime) >= 24 * 60 * 60)) {
|
||||
loadContacts(false, true);
|
||||
return;
|
||||
}
|
||||
|
@ -1021,7 +1041,8 @@ public class ContactsController {
|
|||
|
||||
final HashMap<String, TLRPC.TL_contact> contactsByPhonesDictFinal = contactsByPhonesDict;
|
||||
|
||||
for (TLRPC.TL_contact value : contactsArr) {
|
||||
for (int a = 0; a < contactsArr.size(); a++) {
|
||||
TLRPC.TL_contact value = contactsArr.get(a);
|
||||
TLRPC.User user = usersDict.get(value.user_id);
|
||||
if (user == null) {
|
||||
continue;
|
||||
|
@ -1354,7 +1375,8 @@ public class ContactsController {
|
|||
if (newC == null || contactsTD == null) {
|
||||
newC = new ArrayList<>();
|
||||
contactsTD = new ArrayList<>();
|
||||
for (Integer uid : ids) {
|
||||
for (int a = 0; a < ids.size(); a++) {
|
||||
Integer uid = ids.get(a);
|
||||
if (uid > 0) {
|
||||
TLRPC.TL_contact contact = new TLRPC.TL_contact();
|
||||
contact.user_id = uid;
|
||||
|
@ -1370,7 +1392,8 @@ public class ContactsController {
|
|||
StringBuilder toDelete = new StringBuilder();
|
||||
boolean reloadContacts = false;
|
||||
|
||||
for (TLRPC.TL_contact newContact : newC) {
|
||||
for (int a = 0; a < newC.size(); a++) {
|
||||
TLRPC.TL_contact newContact = newC.get(a);
|
||||
TLRPC.User user = null;
|
||||
if (userDict != null) {
|
||||
user = userDict.get(newContact.user_id);
|
||||
|
@ -1398,7 +1421,8 @@ public class ContactsController {
|
|||
toAdd.append(user.phone);
|
||||
}
|
||||
|
||||
for (final Integer uid : contactsTD) {
|
||||
for (int a = 0; a < contactsTD.size(); a++) {
|
||||
final Integer uid = contactsTD.get(a);
|
||||
Utilities.phoneBookQueue.postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -1452,13 +1476,15 @@ public class ContactsController {
|
|||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (TLRPC.TL_contact contact : newContacts) {
|
||||
for (int a = 0; a < newContacts.size(); a++) {
|
||||
TLRPC.TL_contact contact = newContacts.get(a);
|
||||
if (contactsDict.get(contact.user_id) == null) {
|
||||
contacts.add(contact);
|
||||
contactsDict.put(contact.user_id, contact);
|
||||
}
|
||||
}
|
||||
for (Integer uid : contactsToDelete) {
|
||||
for (int a = 0; a < contactsToDelete.size(); a++) {
|
||||
Integer uid = contactsToDelete.get(a);
|
||||
TLRPC.TL_contact contact = contactsDict.get(uid);
|
||||
if (contact != null) {
|
||||
contacts.remove(contact);
|
||||
|
@ -1469,7 +1495,7 @@ public class ContactsController {
|
|||
updateUnregisteredContacts(contacts);
|
||||
performWriteContactsToPhoneBook();
|
||||
}
|
||||
performSyncPhoneBook(getContactsCopy(contactsBook), false, false, false);
|
||||
performSyncPhoneBook(getContactsCopy(contactsBook), false, false, false, false);
|
||||
buildContactsSectionsArrays(!newContacts.isEmpty());
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.contactsDidLoaded);
|
||||
}
|
||||
|
@ -1643,7 +1669,7 @@ public class ContactsController {
|
|||
if (error != null) {
|
||||
return;
|
||||
}
|
||||
final TLRPC.TL_contacts_importedContacts res = (TLRPC.TL_contacts_importedContacts)response;
|
||||
final TLRPC.TL_contacts_importedContacts res = (TLRPC.TL_contacts_importedContacts) response;
|
||||
MessagesStorage.getInstance().putUsersAndChats(res.users, null, true, true);
|
||||
|
||||
/*if (BuildVars.DEBUG_VERSION) {
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* This is the source code of Telegram for Android v. 3.x.x.
|
||||
* It is licensed under GNU GPL v. 2 or later.
|
||||
* You should have received a copy of the license in this archive (see LICENSE).
|
||||
*
|
||||
* Copyright Nikolai Kudashov, 2013-2016.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger;
|
||||
|
||||
import org.telegram.tgnet.TLRPC;
|
||||
|
||||
public class DialogObject {
|
||||
|
||||
public static boolean isChannel(TLRPC.TL_dialog dialog) {
|
||||
return dialog != null && (dialog.flags & 1) != 0;
|
||||
}
|
||||
}
|
0
TMessagesProj/src/main/java/org/telegram/messenger/GcmInstanceIDListenerService.java
Normal file → Executable file
0
TMessagesProj/src/main/java/org/telegram/messenger/GcmInstanceIDListenerService.java
Normal file → Executable file
0
TMessagesProj/src/main/java/org/telegram/messenger/GcmPushListenerService.java
Normal file → Executable file
0
TMessagesProj/src/main/java/org/telegram/messenger/GcmPushListenerService.java
Normal file → Executable file
0
TMessagesProj/src/main/java/org/telegram/messenger/GcmRegistrationIntentService.java
Normal file → Executable file
0
TMessagesProj/src/main/java/org/telegram/messenger/GcmRegistrationIntentService.java
Normal file → Executable file
|
@ -79,7 +79,6 @@ public class ImageLoader {
|
|||
private HashMap<String, Runnable> retryHttpsTasks = new HashMap<>();
|
||||
private int currentHttpFileLoadTasksCount = 0;
|
||||
|
||||
public VMRuntimeHack runtimeHack = null;
|
||||
private String ignoreRemoval = null;
|
||||
|
||||
private volatile long lastCacheOutTime = 0;
|
||||
|
@ -631,7 +630,7 @@ public class ImageLoader {
|
|||
BitmapFactory.Options opts = new BitmapFactory.Options();
|
||||
opts.inSampleSize = 1;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 14 && Build.VERSION.SDK_INT < 21) {
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
opts.inPurgeable = true;
|
||||
}
|
||||
|
||||
|
@ -685,9 +684,6 @@ public class ImageLoader {
|
|||
} else if (blurType == 0 && opts.inPurgeable) {
|
||||
Utilities.pinBitmap(image);
|
||||
}
|
||||
if (runtimeHack != null) {
|
||||
runtimeHack.trackFree(image.getRowBytes() * image.getHeight());
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
FileLog.e("tmessages", e);
|
||||
|
@ -715,9 +711,6 @@ public class ImageLoader {
|
|||
}
|
||||
|
||||
int delay = 20;
|
||||
if (runtimeHack != null) {
|
||||
delay = 60;
|
||||
}
|
||||
if (mediaId != null) {
|
||||
delay = 0;
|
||||
}
|
||||
|
@ -782,7 +775,7 @@ public class ImageLoader {
|
|||
} else {
|
||||
opts.inPreferredConfig = Bitmap.Config.RGB_565;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 14 && Build.VERSION.SDK_INT < 21) {
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
opts.inPurgeable = true;
|
||||
}
|
||||
|
||||
|
@ -850,9 +843,6 @@ public class ImageLoader {
|
|||
if (!blured && opts.inPurgeable) {
|
||||
Utilities.pinBitmap(image);
|
||||
}
|
||||
if (runtimeHack != null && image != null) {
|
||||
runtimeHack.trackFree(image.getRowBytes() * image.getHeight());
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
//don't promt
|
||||
|
@ -877,9 +867,6 @@ public class ImageLoader {
|
|||
toSet = bitmapDrawable;
|
||||
} else {
|
||||
Bitmap image = bitmapDrawable.getBitmap();
|
||||
if (runtimeHack != null) {
|
||||
runtimeHack.trackAlloc(image.getRowBytes() * image.getHeight());
|
||||
}
|
||||
image.recycle();
|
||||
}
|
||||
}
|
||||
|
@ -1098,19 +1085,10 @@ public class ImageLoader {
|
|||
|
||||
int cacheSize = Math.min(15, ((ActivityManager) ApplicationLoader.applicationContext.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass() / 7) * 1024 * 1024;
|
||||
|
||||
if (Build.VERSION.SDK_INT < 11) {
|
||||
runtimeHack = new VMRuntimeHack();
|
||||
cacheSize = 1024 * 1024 * 3;
|
||||
}
|
||||
memCache = new LruCache(cacheSize) {
|
||||
@Override
|
||||
protected int sizeOf(String key, BitmapDrawable value) {
|
||||
Bitmap b = value.getBitmap();
|
||||
if (Build.VERSION.SDK_INT < 12) {
|
||||
return b.getRowBytes() * b.getHeight();
|
||||
} else {
|
||||
return b.getByteCount();
|
||||
}
|
||||
return value.getBitmap().getByteCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1121,9 +1099,6 @@ public class ImageLoader {
|
|||
final Integer count = bitmapUseCounts.get(key);
|
||||
if (count == null || count == 0) {
|
||||
Bitmap b = oldValue.getBitmap();
|
||||
if (runtimeHack != null) {
|
||||
runtimeHack.trackAlloc(b.getRowBytes() * b.getHeight());
|
||||
}
|
||||
if (!b.isRecycled()) {
|
||||
b.recycle();
|
||||
}
|
||||
|
@ -1937,11 +1912,7 @@ public class ImageLoader {
|
|||
}
|
||||
while (currentHttpTasksCount < 4 && !httpTasks.isEmpty()) {
|
||||
HttpImageTask task = httpTasks.poll();
|
||||
if (android.os.Build.VERSION.SDK_INT >= 11) {
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null, null, null);
|
||||
} else {
|
||||
task.execute(null, null, null);
|
||||
}
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null, null, null);
|
||||
currentHttpTasksCount++;
|
||||
}
|
||||
}
|
||||
|
@ -2006,11 +1977,7 @@ public class ImageLoader {
|
|||
}
|
||||
while (currentHttpFileLoadTasksCount < 2 && !httpFileLoadTasks.isEmpty()) {
|
||||
HttpFileTask task = httpFileLoadTasks.poll();
|
||||
if (android.os.Build.VERSION.SDK_INT >= 11) {
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null, null, null);
|
||||
} else {
|
||||
task.execute(null, null, null);
|
||||
}
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null, null, null);
|
||||
currentHttpFileLoadTasksCount++;
|
||||
}
|
||||
}
|
||||
|
@ -2057,7 +2024,7 @@ public class ImageLoader {
|
|||
}
|
||||
bmOptions.inJustDecodeBounds = false;
|
||||
bmOptions.inSampleSize = (int) scaleFactor;
|
||||
bmOptions.inPurgeable = Build.VERSION.SDK_INT >= 14 && Build.VERSION.SDK_INT < 21;
|
||||
bmOptions.inPurgeable = Build.VERSION.SDK_INT < 21;
|
||||
|
||||
String exifPath = null;
|
||||
if (path != null) {
|
||||
|
|
|
@ -652,6 +652,8 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
|
|||
public Bitmap getBitmap() {
|
||||
if (currentImage instanceof AnimatedFileDrawable) {
|
||||
return ((AnimatedFileDrawable) currentImage).getAnimatedBitmap();
|
||||
} else if (staticThumb instanceof AnimatedFileDrawable) {
|
||||
return ((AnimatedFileDrawable) staticThumb).getAnimatedBitmap();
|
||||
} else if (currentImage instanceof BitmapDrawable) {
|
||||
return ((BitmapDrawable) currentImage).getBitmap();
|
||||
} else if (currentThumb instanceof BitmapDrawable) {
|
||||
|
@ -665,6 +667,8 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
|
|||
public int getBitmapWidth() {
|
||||
if (currentImage instanceof AnimatedFileDrawable) {
|
||||
return orientation % 360 == 0 || orientation % 360 == 180 ? currentImage.getIntrinsicWidth() : currentImage.getIntrinsicHeight();
|
||||
} else if (staticThumb instanceof AnimatedFileDrawable) {
|
||||
return orientation % 360 == 0 || orientation % 360 == 180 ? staticThumb.getIntrinsicWidth() : staticThumb.getIntrinsicHeight();
|
||||
}
|
||||
Bitmap bitmap = getBitmap();
|
||||
return orientation % 360 == 0 || orientation % 360 == 180 ? bitmap.getWidth() : bitmap.getHeight();
|
||||
|
@ -673,6 +677,8 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
|
|||
public int getBitmapHeight() {
|
||||
if (currentImage instanceof AnimatedFileDrawable) {
|
||||
return orientation % 360 == 0 || orientation % 360 == 180 ? currentImage.getIntrinsicHeight() : currentImage.getIntrinsicWidth();
|
||||
} else if (staticThumb instanceof AnimatedFileDrawable) {
|
||||
return orientation % 360 == 0 || orientation % 360 == 180 ? staticThumb.getIntrinsicHeight() : staticThumb.getIntrinsicWidth();
|
||||
}
|
||||
Bitmap bitmap = getBitmap();
|
||||
return orientation % 360 == 0 || orientation % 360 == 180 ? bitmap.getHeight() : bitmap.getWidth();
|
||||
|
@ -872,6 +878,10 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
|
|||
return currentImage instanceof AnimatedFileDrawable && ((AnimatedFileDrawable) currentImage).isRunning();
|
||||
}
|
||||
|
||||
public AnimatedFileDrawable getAnimation() {
|
||||
return currentImage instanceof AnimatedFileDrawable ? (AnimatedFileDrawable) currentImage : null;
|
||||
}
|
||||
|
||||
protected Integer getTag(boolean thumb) {
|
||||
if (thumb) {
|
||||
return thumbTag;
|
||||
|
@ -990,15 +1000,11 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
|
|||
if (key != null && (newKey == null || !newKey.equals(key)) && image != null) {
|
||||
if (image instanceof AnimatedFileDrawable) {
|
||||
AnimatedFileDrawable fileDrawable = (AnimatedFileDrawable) image;
|
||||
fileDrawable.stop();
|
||||
fileDrawable.recycle();
|
||||
} else if (image instanceof BitmapDrawable) {
|
||||
Bitmap bitmap = ((BitmapDrawable) image).getBitmap();
|
||||
boolean canDelete = ImageLoader.getInstance().decrementUseCount(key);
|
||||
if (!ImageLoader.getInstance().isInCache(key)) {
|
||||
if (ImageLoader.getInstance().runtimeHack != null) {
|
||||
ImageLoader.getInstance().runtimeHack.trackAlloc(bitmap.getRowBytes() * bitmap.getHeight());
|
||||
}
|
||||
if (canDelete) {
|
||||
bitmap.recycle();
|
||||
}
|
||||
|
|
|
@ -323,7 +323,6 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
private long recordTimeCount;
|
||||
private long recordDialogId;
|
||||
private MessageObject recordReplyingMessageObject;
|
||||
private boolean recordAsAdmin;
|
||||
private DispatchQueue fileDecodingQueue;
|
||||
private DispatchQueue playerQueue;
|
||||
private ArrayList<AudioBuffer> usedPlayerBuffers = new ArrayList<>();
|
||||
|
@ -605,12 +604,12 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
fileDecodingQueue = new DispatchQueue("fileDecodingQueue");
|
||||
|
||||
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
|
||||
mobileDataDownloadMask = preferences.getInt("mobileDataDownloadMask", AUTODOWNLOAD_MASK_PHOTO | AUTODOWNLOAD_MASK_AUDIO | AUTODOWNLOAD_MASK_MUSIC | (Build.VERSION.SDK_INT >= 11 ? AUTODOWNLOAD_MASK_GIF : 0));
|
||||
wifiDownloadMask = preferences.getInt("wifiDownloadMask", AUTODOWNLOAD_MASK_PHOTO | AUTODOWNLOAD_MASK_AUDIO | AUTODOWNLOAD_MASK_MUSIC | (Build.VERSION.SDK_INT >= 11 ? AUTODOWNLOAD_MASK_GIF : 0));
|
||||
mobileDataDownloadMask = preferences.getInt("mobileDataDownloadMask", AUTODOWNLOAD_MASK_PHOTO | AUTODOWNLOAD_MASK_AUDIO | AUTODOWNLOAD_MASK_MUSIC | AUTODOWNLOAD_MASK_GIF);
|
||||
wifiDownloadMask = preferences.getInt("wifiDownloadMask", AUTODOWNLOAD_MASK_PHOTO | AUTODOWNLOAD_MASK_AUDIO | AUTODOWNLOAD_MASK_MUSIC | AUTODOWNLOAD_MASK_GIF);
|
||||
roamingDownloadMask = preferences.getInt("roamingDownloadMask", 0);
|
||||
saveToGallery = preferences.getBoolean("save_gallery", false);
|
||||
autoplayGifs = preferences.getBoolean("autoplay_gif", true) && Build.VERSION.SDK_INT >= 11;
|
||||
raiseToSpeak = preferences.getBoolean("raise_to_speak", true) && Build.VERSION.SDK_INT >= 11;
|
||||
autoplayGifs = preferences.getBoolean("autoplay_gif", true);
|
||||
raiseToSpeak = preferences.getBoolean("raise_to_speak", true);
|
||||
customTabs = preferences.getBoolean("custom_tabs", true);
|
||||
directShare = preferences.getBoolean("direct_share", true);
|
||||
shuffleMusic = preferences.getBoolean("shuffleMusic", false);
|
||||
|
@ -1085,9 +1084,6 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
}
|
||||
|
||||
public void startMediaObserver() {
|
||||
if (android.os.Build.VERSION.SDK_INT < 14) {
|
||||
return;
|
||||
}
|
||||
ApplicationLoader.applicationHandler.removeCallbacks(stopMediaObserverRunnable);
|
||||
startObserverToken++;
|
||||
try {
|
||||
|
@ -1107,9 +1103,6 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
}
|
||||
|
||||
public void stopMediaObserver() {
|
||||
if (android.os.Build.VERSION.SDK_INT < 14) {
|
||||
return;
|
||||
}
|
||||
if (stopMediaObserverRunnable == null) {
|
||||
stopMediaObserverRunnable = new StopMediaObserverRunnable();
|
||||
}
|
||||
|
@ -1655,7 +1648,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
if (!raiseChat.playFirstUnreadVoiceMessage()) {
|
||||
raiseToEarRecord = true;
|
||||
useFrontSpeaker = false;
|
||||
startRecording(raiseChat.getDialogId(), null, false);
|
||||
startRecording(raiseChat.getDialogId(), null);
|
||||
}
|
||||
ignoreOnPause = true;
|
||||
if (proximityHasDifferentValues && proximityWakeLock != null && !proximityWakeLock.isHeld()) {
|
||||
|
@ -1720,7 +1713,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
return;
|
||||
}
|
||||
raiseToEarRecord = true;
|
||||
startRecording(raiseChat.getDialogId(), null, false);
|
||||
startRecording(raiseChat.getDialogId(), null);
|
||||
ignoreOnPause = true;
|
||||
}
|
||||
|
||||
|
@ -2025,6 +2018,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
if (currentPlaylistNum == -1) {
|
||||
playlist.clear();
|
||||
shuffledPlaylist.clear();
|
||||
currentPlaylistNum = playlist.size();
|
||||
playlist.add(current);
|
||||
}
|
||||
if (current.isMusic()) {
|
||||
|
@ -2540,7 +2534,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
return downloadingCurrentMessage;
|
||||
}
|
||||
|
||||
public void startRecording(final long dialog_id, final MessageObject reply_to_msg, final boolean asAdmin) {
|
||||
public void startRecording(final long dialog_id, final MessageObject reply_to_msg) {
|
||||
boolean paused = false;
|
||||
if (playingMessageObject != null && isPlayingAudio(playingMessageObject) && !isAudioPaused()) {
|
||||
paused = true;
|
||||
|
@ -2592,17 +2586,13 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
});
|
||||
return;
|
||||
}
|
||||
//if (Build.VERSION.SDK_INT >= 11) {
|
||||
// audioRecorder = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, 16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, recordBufferSize * 10);
|
||||
//} else {
|
||||
audioRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, recordBufferSize * 10);
|
||||
//}
|
||||
|
||||
audioRecorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, recordBufferSize * 10);
|
||||
recordStartTime = System.currentTimeMillis();
|
||||
recordTimeCount = 0;
|
||||
samplesCount = 0;
|
||||
recordDialogId = dialog_id;
|
||||
recordReplyingMessageObject = reply_to_msg;
|
||||
recordAsAdmin = asAdmin;
|
||||
fileBuffer.rewind();
|
||||
|
||||
audioRecorder.startRecording();
|
||||
|
@ -2670,7 +2660,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
}
|
||||
TLRPC.TL_messages_messages messagesRes = new TLRPC.TL_messages_messages();
|
||||
messagesRes.messages.add(messageObject.messageOwner);
|
||||
MessagesStorage.getInstance().putMessages(messagesRes, messageObject.getDialogId(), -1, 0, 0, false);
|
||||
MessagesStorage.getInstance().putMessages(messagesRes, messageObject.getDialogId(), -1, 0, false);
|
||||
ArrayList<MessageObject> arrayList = new ArrayList<>();
|
||||
arrayList.add(messageObject);
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.replaceMessagesObjects, messageObject.getDialogId(), arrayList);
|
||||
|
@ -2705,7 +2695,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
audioToSend.attributes.add(attributeAudio);
|
||||
if (duration > 700) {
|
||||
if (send == 1) {
|
||||
SendMessagesHelper.getInstance().sendMessage(audioToSend, null, recordingAudioFileToSend.getAbsolutePath(), recordDialogId, recordReplyingMessageObject, recordAsAdmin, null, null);
|
||||
SendMessagesHelper.getInstance().sendMessage(audioToSend, null, recordingAudioFileToSend.getAbsolutePath(), recordDialogId, recordReplyingMessageObject, null, null);
|
||||
}
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioDidSent, send == 2 ? audioToSend : null, send == 2 ? recordingAudioFileToSend.getAbsolutePath() : null);
|
||||
} else {
|
||||
|
@ -2866,10 +2856,8 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
|
||||
if (result) {
|
||||
if (type == 2) {
|
||||
if (Build.VERSION.SDK_INT >= 12) {
|
||||
DownloadManager downloadManager = (DownloadManager) ApplicationLoader.applicationContext.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
downloadManager.addCompletedDownload(destFile.getName(), destFile.getName(), false, mime, destFile.getAbsolutePath(), destFile.length(), true);
|
||||
}
|
||||
DownloadManager downloadManager = (DownloadManager) ApplicationLoader.applicationContext.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
downloadManager.addCompletedDownload(destFile.getName(), destFile.getName(), false, mime, destFile.getAbsolutePath(), destFile.length(), true);
|
||||
} else {
|
||||
AndroidUtilities.addMediaToGallery(Uri.fromFile(destFile));
|
||||
}
|
||||
|
|
|
@ -314,6 +314,8 @@ public class MessageObject {
|
|||
messageText = LocaleController.getString("ActionMigrateFromGroup", R.string.ActionMigrateFromGroup);
|
||||
} else if (message.action instanceof TLRPC.TL_messageActionPinMessage) {
|
||||
generatePinMessageText(fromUser, fromUser == null ? chats.get(message.to_id.channel_id) : null);
|
||||
} else if (message.action instanceof TLRPC.TL_messageActionHistoryClear) {
|
||||
messageText = LocaleController.getString("HistoryCleared", R.string.HistoryCleared);
|
||||
}
|
||||
}
|
||||
} else if (!isMediaEmpty()) {
|
||||
|
@ -370,7 +372,9 @@ public class MessageObject {
|
|||
|
||||
if (messageOwner.message != null && messageOwner.id < 0 && messageOwner.message.length() > 6 && isVideo()) {
|
||||
videoEditedInfo = new VideoEditedInfo();
|
||||
videoEditedInfo.parseString(messageOwner.message);
|
||||
if (!videoEditedInfo.parseString(messageOwner.message)) {
|
||||
videoEditedInfo = null;
|
||||
}
|
||||
}
|
||||
|
||||
generateCaption();
|
||||
|
@ -510,6 +514,9 @@ public class MessageObject {
|
|||
contentType = -1;
|
||||
type = -1;
|
||||
}
|
||||
} else if (messageOwner.action instanceof TLRPC.TL_messageActionHistoryClear) {
|
||||
contentType = -1;
|
||||
type = -1;
|
||||
} else {
|
||||
contentType = 1;
|
||||
type = 10;
|
||||
|
@ -1199,27 +1206,10 @@ public class MessageObject {
|
|||
return message.media_unread;
|
||||
}
|
||||
|
||||
public boolean isImportant() {
|
||||
return isImportant(messageOwner);
|
||||
}
|
||||
|
||||
public boolean isMegagroup() {
|
||||
return isMegagroup(messageOwner);
|
||||
}
|
||||
|
||||
public static boolean isImportant(TLRPC.Message message) {
|
||||
if (isMegagroup(message)) {
|
||||
return message.post;
|
||||
}
|
||||
if (message.to_id.channel_id != 0) {
|
||||
if (message instanceof TLRPC.TL_message_layer47 || message instanceof TLRPC.TL_message_old7) {
|
||||
return message.to_id.channel_id != 0 && (message.from_id <= 0 || message.mentioned || message.out || (message.flags & TLRPC.MESSAGE_FLAG_HAS_FROM_ID) == 0);
|
||||
}
|
||||
return message.post;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isMegagroup(TLRPC.Message message) {
|
||||
return (message.flags & TLRPC.MESSAGE_FLAG_MEGAGROUP) != 0;
|
||||
}
|
||||
|
@ -1658,7 +1648,7 @@ public class MessageObject {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if (chat.megagroup && message.out || !chat.megagroup && (chat.creator || chat.editor && isOut(message)) && isImportant(message)) {
|
||||
if (chat.megagroup && message.out || !chat.megagroup && (chat.creator || chat.editor && isOut(message)) && message.post) {
|
||||
if (message.media instanceof TLRPC.TL_messageMediaPhoto ||
|
||||
message.media instanceof TLRPC.TL_messageMediaDocument && !isStickerMessage(message) ||
|
||||
message.media instanceof TLRPC.TL_messageMediaEmpty ||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -190,7 +190,11 @@ public class MusicPlayerService extends Service implements NotificationCenter.No
|
|||
metadataEditor.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, authorName);
|
||||
metadataEditor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, songName);
|
||||
if (audioInfo != null && audioInfo.getCover() != null) {
|
||||
metadataEditor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, audioInfo.getCover());
|
||||
try {
|
||||
metadataEditor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, audioInfo.getCover());
|
||||
} catch (Throwable e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
}
|
||||
metadataEditor.apply();
|
||||
}
|
||||
|
|
|
@ -142,10 +142,12 @@ public class NativeLoader {
|
|||
folder = "x86";
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT == 8) {
|
||||
File destFile = new File(context.getApplicationInfo().dataDir + "/lib", LIB_SO_NAME);
|
||||
|
||||
File destFile = getNativeLibraryDir(context);
|
||||
if (destFile != null) {
|
||||
destFile = new File(destFile, LIB_SO_NAME);
|
||||
if (destFile.exists()) {
|
||||
FileLog.d("tmessages", "Load normal lib");
|
||||
FileLog.d("tmessages", "load normal lib");
|
||||
try {
|
||||
System.loadLibrary(LIB_NAME);
|
||||
init(Constants.FILES_PATH, BuildVars.DEBUG_VERSION);
|
||||
|
@ -154,31 +156,6 @@ public class NativeLoader {
|
|||
} catch (Error e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
System.loadLibrary(LIB_NAME);
|
||||
init(Constants.FILES_PATH, BuildVars.DEBUG_VERSION);
|
||||
nativeLoaded = true;
|
||||
return;
|
||||
} catch (Error e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
File destFile = getNativeLibraryDir(context);
|
||||
if (destFile != null) {
|
||||
destFile = new File(destFile, LIB_SO_NAME);
|
||||
if (destFile.exists()) {
|
||||
FileLog.d("tmessages", "load normal lib");
|
||||
try {
|
||||
System.loadLibrary(LIB_NAME);
|
||||
init(Constants.FILES_PATH, BuildVars.DEBUG_VERSION);
|
||||
nativeLoaded = true;
|
||||
return;
|
||||
} catch (Error e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ public class NotificationCenter {
|
|||
public static final int encryptedChatUpdated = totalEvents++;
|
||||
public static final int messagesReadEncrypted = totalEvents++;
|
||||
public static final int encryptedChatCreated = totalEvents++;
|
||||
public static final int userPhotosLoaded = totalEvents++;
|
||||
public static final int dialogPhotosLoaded = totalEvents++;
|
||||
public static final int removeAllMessagesFromDialog = totalEvents++;
|
||||
public static final int notificationsSettingsUpdated = totalEvents++;
|
||||
public static final int pushMessagesUpdated = totalEvents++;
|
||||
|
@ -73,6 +73,7 @@ public class NotificationCenter {
|
|||
public static final int wasUnableToFindCurrentLocation = totalEvents++;
|
||||
public static final int reloadHints = totalEvents++;
|
||||
public static final int reloadInlineHints = totalEvents++;
|
||||
public static final int newDraftReceived = totalEvents++;
|
||||
|
||||
public static final int httpFileDidLoaded = totalEvents++;
|
||||
public static final int httpFileDidFailedLoad = totalEvents++;
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* This is the source code of Telegram for Android v. 3.x.x.
|
||||
* It is licensed under GNU GPL v. 2 or later.
|
||||
* You should have received a copy of the license in this archive (see LICENSE).
|
||||
*
|
||||
* Copyright Nikolai Kudashov, 2013-2016.
|
||||
*/
|
||||
|
||||
package org.telegram.messenger;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
public class NotificationDismissReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Context.MODE_PRIVATE);
|
||||
preferences.edit().putInt("dismissDate", intent.getIntExtra("messageDate", 0)).commit();
|
||||
}
|
||||
}
|
|
@ -18,6 +18,8 @@ import android.content.Intent;
|
|||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.media.AudioManager;
|
||||
|
@ -956,7 +958,7 @@ public class NotificationsController {
|
|||
}
|
||||
} else {
|
||||
if (ChatObject.isChannel(chat) && !chat.megagroup) {
|
||||
if (messageObject.isImportant()) {
|
||||
if (messageObject.messageOwner.post) {
|
||||
if (messageObject.isMediaEmpty()) {
|
||||
if (!shortMessage && messageObject.messageOwner.message != null && messageObject.messageOwner.message.length() != 0) {
|
||||
msg = LocaleController.formatString("NotificationMessageGroupText", R.string.NotificationMessageGroupText, name, chat.title, messageObject.messageOwner.message);
|
||||
|
@ -1271,6 +1273,12 @@ public class NotificationsController {
|
|||
ConnectionsManager.getInstance().resumeNetworkMaybe();
|
||||
|
||||
MessageObject lastMessageObject = pushMessages.get(0);
|
||||
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Context.MODE_PRIVATE);
|
||||
int dismissDate = preferences.getInt("dismissDate", 0);
|
||||
if (lastMessageObject.messageOwner.date <= dismissDate) {
|
||||
dismissNotification();
|
||||
return;
|
||||
}
|
||||
|
||||
long dialog_id = lastMessageObject.getDialogId();
|
||||
long override_dialog_id = dialog_id;
|
||||
|
@ -1305,7 +1313,6 @@ public class NotificationsController {
|
|||
int priorityOverride;
|
||||
int vibrateOverride;
|
||||
|
||||
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Context.MODE_PRIVATE);
|
||||
int notifyOverride = getNotifyOverride(preferences, override_dialog_id);
|
||||
if (!notifyAboutLast || notifyOverride == 2 || (!preferences.getBoolean("EnableAll", true) || chat_id != 0 && !preferences.getBoolean("EnableGroup", true)) && notifyOverride == 0) {
|
||||
notifyDisabled = true;
|
||||
|
@ -1475,6 +1482,7 @@ public class NotificationsController {
|
|||
|
||||
int silent = 2;
|
||||
String lastMessage = null;
|
||||
boolean hasNewMessages = false;
|
||||
if (pushMessages.size() == 1) {
|
||||
MessageObject messageObject = pushMessages.get(0);
|
||||
String message = lastMessage = getStringForMessage(messageObject, false);
|
||||
|
@ -1499,7 +1507,7 @@ public class NotificationsController {
|
|||
for (int i = 0; i < count; i++) {
|
||||
MessageObject messageObject = pushMessages.get(i);
|
||||
String message = getStringForMessage(messageObject, false);
|
||||
if (message == null) {
|
||||
if (message == null || messageObject.messageOwner.date <= dismissDate) {
|
||||
continue;
|
||||
}
|
||||
if (silent == 2) {
|
||||
|
@ -1521,10 +1529,26 @@ public class NotificationsController {
|
|||
mBuilder.setStyle(inboxStyle);
|
||||
}
|
||||
|
||||
Intent dismissIntent = new Intent(ApplicationLoader.applicationContext, NotificationDismissReceiver.class);
|
||||
dismissIntent.putExtra("messageDate", lastMessageObject.messageOwner.date);
|
||||
mBuilder.setDeleteIntent(PendingIntent.getBroadcast(ApplicationLoader.applicationContext, 1, dismissIntent, PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
|
||||
if (photoPath != null) {
|
||||
BitmapDrawable img = ImageLoader.getInstance().getImageFromMemory(photoPath, null, "50_50");
|
||||
if (img != null) {
|
||||
mBuilder.setLargeIcon(img.getBitmap());
|
||||
} else {
|
||||
try {
|
||||
float scaleFactor = 160.0f / AndroidUtilities.dp(50);
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inSampleSize = scaleFactor < 1 ? 1 : (int) scaleFactor;
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(FileLoader.getPathToAttach(photoPath, true).toString(), options);
|
||||
if (bitmap != null) {
|
||||
mBuilder.setLargeIcon(bitmap);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1742,6 +1766,7 @@ public class NotificationsController {
|
|||
.setSmallIcon(R.drawable.notification)
|
||||
.setGroup("messages")
|
||||
.setContentText(text)
|
||||
.setAutoCancel(true)
|
||||
.setColor(0xff2ca5e0)
|
||||
.setGroupSummary(false)
|
||||
.setContentIntent(contentIntent)
|
||||
|
|
|
@ -88,7 +88,7 @@ public class SecretChatHelper {
|
|||
return localInstance;
|
||||
}
|
||||
|
||||
public void cleanUp() {
|
||||
public void cleanup() {
|
||||
sendingNotifyLayer.clear();
|
||||
acceptingChats.clear();
|
||||
secretHolesQueue.clear();
|
||||
|
@ -194,7 +194,7 @@ public class SecretChatHelper {
|
|||
user = usersDict.get(user_id);
|
||||
}
|
||||
newChat.user_id = user_id;
|
||||
final TLRPC.Dialog dialog = new TLRPC.TL_dialog();
|
||||
final TLRPC.TL_dialog dialog = new TLRPC.TL_dialog();
|
||||
dialog.id = dialog_id;
|
||||
dialog.unread_count = 0;
|
||||
dialog.top_message = 0;
|
||||
|
@ -206,18 +206,7 @@ public class SecretChatHelper {
|
|||
MessagesController.getInstance().dialogs_dict.put(dialog.id, dialog);
|
||||
MessagesController.getInstance().dialogs.add(dialog);
|
||||
MessagesController.getInstance().putEncryptedChat(newChat, false);
|
||||
Collections.sort(MessagesController.getInstance().dialogs, new Comparator<TLRPC.Dialog>() {
|
||||
@Override
|
||||
public int compare(TLRPC.Dialog tl_dialog, TLRPC.Dialog tl_dialog2) {
|
||||
if (tl_dialog.last_message_date == tl_dialog2.last_message_date) {
|
||||
return 0;
|
||||
} else if (tl_dialog.last_message_date < tl_dialog2.last_message_date) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
});
|
||||
MessagesController.getInstance().sortDialogs(null);
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload);
|
||||
}
|
||||
});
|
||||
|
@ -1153,7 +1142,7 @@ public class SecretChatHelper {
|
|||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
TLRPC.Dialog dialog = MessagesController.getInstance().dialogs_dict.get(did);
|
||||
TLRPC.TL_dialog dialog = MessagesController.getInstance().dialogs_dict.get(did);
|
||||
if (dialog != null) {
|
||||
dialog.unread_count = 0;
|
||||
MessagesController.getInstance().dialogMessage.remove(dialog.id);
|
||||
|
@ -1813,25 +1802,14 @@ public class SecretChatHelper {
|
|||
chat.seq_out = 1;
|
||||
chat.a_or_b = salt;
|
||||
MessagesController.getInstance().putEncryptedChat(chat, false);
|
||||
TLRPC.Dialog dialog = new TLRPC.TL_dialog();
|
||||
TLRPC.TL_dialog dialog = new TLRPC.TL_dialog();
|
||||
dialog.id = ((long) chat.id) << 32;
|
||||
dialog.unread_count = 0;
|
||||
dialog.top_message = 0;
|
||||
dialog.last_message_date = ConnectionsManager.getInstance().getCurrentTime();
|
||||
MessagesController.getInstance().dialogs_dict.put(dialog.id, dialog);
|
||||
MessagesController.getInstance().dialogs.add(dialog);
|
||||
Collections.sort(MessagesController.getInstance().dialogs, new Comparator<TLRPC.Dialog>() {
|
||||
@Override
|
||||
public int compare(TLRPC.Dialog tl_dialog, TLRPC.Dialog tl_dialog2) {
|
||||
if (tl_dialog.last_message_date == tl_dialog2.last_message_date) {
|
||||
return 0;
|
||||
} else if (tl_dialog.last_message_date < tl_dialog2.last_message_date) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
});
|
||||
MessagesController.getInstance().sortDialogs(null);
|
||||
MessagesStorage.getInstance().putEncryptedChat(chat, user, dialog);
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload);
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.encryptedChatCreated, chat);
|
||||
|
@ -1839,7 +1817,7 @@ public class SecretChatHelper {
|
|||
@Override
|
||||
public void run() {
|
||||
if (!delayedEncryptedChatUpdates.isEmpty()) {
|
||||
MessagesController.getInstance().processUpdateArray(delayedEncryptedChatUpdates, null, null);
|
||||
MessagesController.getInstance().processUpdateArray(delayedEncryptedChatUpdates, null, null, false);
|
||||
delayedEncryptedChatUpdates.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,13 +20,13 @@ import android.media.MediaMetadataRetriever;
|
|||
import android.media.MediaPlayer;
|
||||
import android.media.ThumbnailUtils;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.MediaStore;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.telegram.messenger.audioinfo.AudioInfo;
|
||||
import org.telegram.messenger.query.DraftQuery;
|
||||
import org.telegram.messenger.query.SearchQuery;
|
||||
import org.telegram.messenger.query.StickersQuery;
|
||||
import org.telegram.tgnet.ConnectionsManager;
|
||||
|
@ -233,7 +233,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.FileDidFailedLoad);
|
||||
}
|
||||
|
||||
public void cleanUp() {
|
||||
public void cleanup() {
|
||||
delayedMessages.clear();
|
||||
unsentMessages.clear();
|
||||
sendingMessages.clear();
|
||||
|
@ -587,7 +587,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
if (unsent) {
|
||||
unsentMessages.put(messageObject.getId(), messageObject);
|
||||
}
|
||||
sendMessage(messageObject, messageObject.messageOwner.post);
|
||||
sendMessage(messageObject);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -599,43 +599,43 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
}
|
||||
}
|
||||
|
||||
public void processForwardFromMyName(MessageObject messageObject, long did, boolean asAdmin) {
|
||||
public void processForwardFromMyName(MessageObject messageObject, long did) {
|
||||
if (messageObject == null) {
|
||||
return;
|
||||
}
|
||||
if (messageObject.messageOwner.media != null && !(messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaEmpty) && !(messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaWebPage)) {
|
||||
if (messageObject.messageOwner.media.photo instanceof TLRPC.TL_photo) {
|
||||
sendMessage((TLRPC.TL_photo) messageObject.messageOwner.media.photo, null, did, messageObject.replyMessageObject, asAdmin, null, null);
|
||||
sendMessage((TLRPC.TL_photo) messageObject.messageOwner.media.photo, null, did, messageObject.replyMessageObject, null, null);
|
||||
} else if (messageObject.messageOwner.media.document instanceof TLRPC.TL_document) {
|
||||
sendMessage((TLRPC.TL_document) messageObject.messageOwner.media.document, null, messageObject.messageOwner.attachPath, did, messageObject.replyMessageObject, asAdmin, null, null);
|
||||
sendMessage((TLRPC.TL_document) messageObject.messageOwner.media.document, null, messageObject.messageOwner.attachPath, did, messageObject.replyMessageObject, null, null);
|
||||
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo) {
|
||||
sendMessage(messageObject.messageOwner.media, did, messageObject.replyMessageObject, asAdmin, null, null);
|
||||
sendMessage(messageObject.messageOwner.media, did, messageObject.replyMessageObject, null, null);
|
||||
} else if (messageObject.messageOwner.media.phone_number != null) {
|
||||
TLRPC.User user = new TLRPC.TL_userContact_old2();
|
||||
user.phone = messageObject.messageOwner.media.phone_number;
|
||||
user.first_name = messageObject.messageOwner.media.first_name;
|
||||
user.last_name = messageObject.messageOwner.media.last_name;
|
||||
user.id = messageObject.messageOwner.media.user_id;
|
||||
sendMessage(user, did, messageObject.replyMessageObject, asAdmin, null, null);
|
||||
sendMessage(user, did, messageObject.replyMessageObject, null, null);
|
||||
} else {
|
||||
ArrayList<MessageObject> arrayList = new ArrayList<>();
|
||||
arrayList.add(messageObject);
|
||||
sendMessage(arrayList, did, asAdmin);
|
||||
sendMessage(arrayList, did);
|
||||
}
|
||||
} else if (messageObject.messageOwner.message != null) {
|
||||
TLRPC.WebPage webPage = null;
|
||||
if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaWebPage) {
|
||||
webPage = messageObject.messageOwner.media.webpage;
|
||||
}
|
||||
sendMessage(messageObject.messageOwner.message, did, messageObject.replyMessageObject, webPage, true, asAdmin, messageObject.messageOwner.entities, null, null);
|
||||
sendMessage(messageObject.messageOwner.message, did, messageObject.replyMessageObject, webPage, true, messageObject.messageOwner.entities, null, null);
|
||||
} else {
|
||||
ArrayList<MessageObject> arrayList = new ArrayList<>();
|
||||
arrayList.add(messageObject);
|
||||
sendMessage(arrayList, did, asAdmin);
|
||||
sendMessage(arrayList, did);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendSticker(TLRPC.Document document, long peer, MessageObject replyingMessageObject, boolean asAdmin) {
|
||||
public void sendSticker(TLRPC.Document document, long peer, MessageObject replyingMessageObject) {
|
||||
if (document == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -679,10 +679,10 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
}
|
||||
}
|
||||
}
|
||||
SendMessagesHelper.getInstance().sendMessage((TLRPC.TL_document) document, null, null, peer, replyingMessageObject, asAdmin, null, null);
|
||||
SendMessagesHelper.getInstance().sendMessage((TLRPC.TL_document) document, null, null, peer, replyingMessageObject, null, null);
|
||||
}
|
||||
|
||||
public void sendMessage(ArrayList<MessageObject> messages, final long peer, boolean asAdmin) {
|
||||
public void sendMessage(ArrayList<MessageObject> messages, final long peer) {
|
||||
if ((int) peer == 0 || messages == null || messages.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -762,7 +762,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
}
|
||||
newMsg.local_id = newMsg.id = UserConfig.getNewMessageId();
|
||||
newMsg.out = true;
|
||||
if (asAdmin && to_id.channel_id != 0 && !isMegagroup) {
|
||||
if (to_id.channel_id != 0 && !isMegagroup) {
|
||||
newMsg.from_id = isSignature ? UserConfig.getClientUserId() : -to_id.channel_id;
|
||||
newMsg.post = true;
|
||||
} else {
|
||||
|
@ -777,9 +777,11 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
ids.add(newMsg.fwd_msg_id);
|
||||
newMsg.date = ConnectionsManager.getInstance().getCurrentTime();
|
||||
if (inputPeer instanceof TLRPC.TL_inputPeerChannel) {
|
||||
if (asAdmin && !isMegagroup) {
|
||||
if (!isMegagroup) {
|
||||
newMsg.views = 1;
|
||||
newMsg.flags |= TLRPC.MESSAGE_FLAG_HAS_VIEWS;
|
||||
} else {
|
||||
newMsg.unread = true;
|
||||
}
|
||||
} else {
|
||||
if ((msgObj.messageOwner.flags & TLRPC.MESSAGE_FLAG_HAS_VIEWS) != 0) {
|
||||
|
@ -832,10 +834,6 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
req.random_id = randomIds;
|
||||
req.id = ids;
|
||||
|
||||
if (asAdmin && req.to_peer.channel_id != 0 && !isMegagroup) {
|
||||
req.broadcast = true;
|
||||
}
|
||||
|
||||
final ArrayList<TLRPC.Message> newMsgObjArr = arr;
|
||||
final ArrayList<MessageObject> newMsgArr = objArr;
|
||||
final HashMap<Long, TLRPC.Message> messagesByRandomIdsFinal = messagesByRandomIds;
|
||||
|
@ -855,6 +853,12 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
a--;
|
||||
}
|
||||
}
|
||||
Integer value = MessagesController.getInstance().dialogs_read_outbox_max.get(peer);
|
||||
if (value == null) {
|
||||
value = MessagesStorage.getInstance().getDialogReadMax(true, peer);
|
||||
MessagesController.getInstance().dialogs_read_outbox_max.put(peer, value);
|
||||
}
|
||||
|
||||
for (int a = 0; a < updates.updates.size(); a++) {
|
||||
TLRPC.Update update = updates.updates.get(a);
|
||||
if (update instanceof TLRPC.TL_updateNewMessage || update instanceof TLRPC.TL_updateNewChannelMessage) {
|
||||
|
@ -869,6 +873,8 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
message.flags |= TLRPC.MESSAGE_FLAG_MEGAGROUP;
|
||||
}
|
||||
}
|
||||
message.unread = value < message.id;
|
||||
|
||||
Long random_id = newMessagesByIds.get(message.id);
|
||||
if (random_id != null) {
|
||||
final TLRPC.Message newMsgObj = messagesByRandomIdsFinal.get(random_id);
|
||||
|
@ -996,7 +1002,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
mediaGeo.geo._long = location.getLongitude();
|
||||
for (HashMap.Entry<String, MessageObject> entry : waitingForLocation.entrySet()) {
|
||||
MessageObject messageObject = entry.getValue();
|
||||
SendMessagesHelper.getInstance().sendMessage(mediaGeo, messageObject.getDialogId(), messageObject, false, null, null);
|
||||
SendMessagesHelper.getInstance().sendMessage(mediaGeo, messageObject.getDialogId(), messageObject, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1062,31 +1068,31 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
return !(messageObject == null || button == null) && waitingForCallback.containsKey(messageObject.getId() + "_" + Utilities.bytesToHex(button.data));
|
||||
}
|
||||
|
||||
public void sendMessage(MessageObject retryMessageObject, boolean asAdmin) {
|
||||
sendMessage(null, null, null, null, null, null, retryMessageObject.getDialogId(), retryMessageObject.messageOwner.attachPath, null, null, true, asAdmin, retryMessageObject, null, retryMessageObject.messageOwner.reply_markup, retryMessageObject.messageOwner.params);
|
||||
public void sendMessage(MessageObject retryMessageObject) {
|
||||
sendMessage(null, null, null, null, null, null, retryMessageObject.getDialogId(), retryMessageObject.messageOwner.attachPath, null, null, true, retryMessageObject, null, retryMessageObject.messageOwner.reply_markup, retryMessageObject.messageOwner.params);
|
||||
}
|
||||
|
||||
public void sendMessage(TLRPC.User user, long peer, MessageObject reply_to_msg, boolean asAdmin, TLRPC.ReplyMarkup replyMarkup, HashMap<String, String> params) {
|
||||
sendMessage(null, null, null, null, user, null, peer, null, reply_to_msg, null, true, asAdmin, null, null, replyMarkup, params);
|
||||
public void sendMessage(TLRPC.User user, long peer, MessageObject reply_to_msg, TLRPC.ReplyMarkup replyMarkup, HashMap<String, String> params) {
|
||||
sendMessage(null, null, null, null, user, null, peer, null, reply_to_msg, null, true, null, null, replyMarkup, params);
|
||||
}
|
||||
|
||||
public void sendMessage(TLRPC.TL_document document, VideoEditedInfo videoEditedInfo, String path, long peer, MessageObject reply_to_msg, boolean asAdmin, TLRPC.ReplyMarkup replyMarkup, HashMap<String, String> params) {
|
||||
sendMessage(null, null, null, videoEditedInfo, null, document, peer, path, reply_to_msg, null, true, asAdmin, null, null, replyMarkup, params);
|
||||
public void sendMessage(TLRPC.TL_document document, VideoEditedInfo videoEditedInfo, String path, long peer, MessageObject reply_to_msg, TLRPC.ReplyMarkup replyMarkup, HashMap<String, String> params) {
|
||||
sendMessage(null, null, null, videoEditedInfo, null, document, peer, path, reply_to_msg, null, true, null, null, replyMarkup, params);
|
||||
}
|
||||
|
||||
public void sendMessage(String message, long peer, MessageObject reply_to_msg, TLRPC.WebPage webPage, boolean searchLinks, boolean asAdmin, ArrayList<TLRPC.MessageEntity> entities, TLRPC.ReplyMarkup replyMarkup, HashMap<String, String> params) {
|
||||
sendMessage(message, null, null, null, null, null, peer, null, reply_to_msg, webPage, searchLinks, asAdmin, null, entities, replyMarkup, params);
|
||||
public void sendMessage(String message, long peer, MessageObject reply_to_msg, TLRPC.WebPage webPage, boolean searchLinks, ArrayList<TLRPC.MessageEntity> entities, TLRPC.ReplyMarkup replyMarkup, HashMap<String, String> params) {
|
||||
sendMessage(message, null, null, null, null, null, peer, null, reply_to_msg, webPage, searchLinks, null, entities, replyMarkup, params);
|
||||
}
|
||||
|
||||
public void sendMessage(TLRPC.MessageMedia location, long peer, MessageObject reply_to_msg, boolean asAdmin, TLRPC.ReplyMarkup replyMarkup, HashMap<String, String> params) {
|
||||
sendMessage(null, location, null, null, null, null, peer, null, reply_to_msg, null, true, asAdmin, null, null, replyMarkup, params);
|
||||
public void sendMessage(TLRPC.MessageMedia location, long peer, MessageObject reply_to_msg, TLRPC.ReplyMarkup replyMarkup, HashMap<String, String> params) {
|
||||
sendMessage(null, location, null, null, null, null, peer, null, reply_to_msg, null, true, null, null, replyMarkup, params);
|
||||
}
|
||||
|
||||
public void sendMessage(TLRPC.TL_photo photo, String path, long peer, MessageObject reply_to_msg, boolean asAdmin, TLRPC.ReplyMarkup replyMarkup, HashMap<String, String> params) {
|
||||
sendMessage(null, null, photo, null, null, null, peer, path, reply_to_msg, null, true, asAdmin, null, null, replyMarkup, params);
|
||||
public void sendMessage(TLRPC.TL_photo photo, String path, long peer, MessageObject reply_to_msg, TLRPC.ReplyMarkup replyMarkup, HashMap<String, String> params) {
|
||||
sendMessage(null, null, photo, null, null, null, peer, path, reply_to_msg, null, true, null, null, replyMarkup, params);
|
||||
}
|
||||
|
||||
private void sendMessage(String message, TLRPC.MessageMedia location, TLRPC.TL_photo photo, VideoEditedInfo videoEditedInfo, TLRPC.User user, TLRPC.TL_document document, long peer, String path, MessageObject reply_to_msg, TLRPC.WebPage webPage, boolean searchLinks, boolean asAdmin, MessageObject retryMessageObject, ArrayList<TLRPC.MessageEntity> entities, TLRPC.ReplyMarkup replyMarkup, HashMap<String, String> params) {
|
||||
private void sendMessage(String message, TLRPC.MessageMedia location, TLRPC.TL_photo photo, VideoEditedInfo videoEditedInfo, TLRPC.User user, TLRPC.TL_document document, long peer, String path, MessageObject reply_to_msg, TLRPC.WebPage webPage, boolean searchLinks, MessageObject retryMessageObject, ArrayList<TLRPC.MessageEntity> entities, TLRPC.ReplyMarkup replyMarkup, HashMap<String, String> params) {
|
||||
if (peer == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -1101,6 +1107,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
int type = -1;
|
||||
int lower_id = (int) peer;
|
||||
int high_id = (int) (peer >> 32);
|
||||
boolean isChannel = false;
|
||||
TLRPC.EncryptedChat encryptedChat = null;
|
||||
TLRPC.InputPeer sendToPeer = lower_id != 0 ? MessagesController.getInputPeer(lower_id) : null;
|
||||
ArrayList<TLRPC.InputUser> sendToPeers = null;
|
||||
|
@ -1115,11 +1122,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
}
|
||||
return;
|
||||
}
|
||||
} else if (asAdmin && sendToPeer instanceof TLRPC.TL_inputPeerChannel) {
|
||||
} else if (sendToPeer instanceof TLRPC.TL_inputPeerChannel) {
|
||||
TLRPC.Chat chat = MessagesController.getInstance().getChat(sendToPeer.channel_id);
|
||||
if (chat.megagroup) {
|
||||
asAdmin = false;
|
||||
}
|
||||
isChannel = chat != null && !chat.megagroup;
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -1304,7 +1309,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
}
|
||||
newMsg.local_id = newMsg.id = UserConfig.getNewMessageId();
|
||||
newMsg.out = true;
|
||||
if (asAdmin && sendToPeer != null && sendToPeer.channel_id != 0) {
|
||||
if (isChannel && sendToPeer != null) {
|
||||
newMsg.from_id = -sendToPeer.channel_id;
|
||||
} else {
|
||||
newMsg.from_id = UserConfig.getClientUserId();
|
||||
|
@ -1330,7 +1335,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
newMsg.date = ConnectionsManager.getInstance().getCurrentTime();
|
||||
newMsg.flags |= TLRPC.MESSAGE_FLAG_HAS_MEDIA;
|
||||
if (sendToPeer instanceof TLRPC.TL_inputPeerChannel) {
|
||||
if (asAdmin) {
|
||||
if (isChannel) {
|
||||
newMsg.views = 1;
|
||||
newMsg.flags |= TLRPC.MESSAGE_FLAG_HAS_VIEWS;
|
||||
}
|
||||
|
@ -1338,6 +1343,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
if (chat != null) {
|
||||
if (chat.megagroup) {
|
||||
newMsg.flags |= TLRPC.MESSAGE_FLAG_MEGAGROUP;
|
||||
newMsg.unread = true;
|
||||
} else {
|
||||
newMsg.post = true;
|
||||
if (chat.signatures) {
|
||||
|
@ -1466,14 +1472,12 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
} else {
|
||||
TLRPC.TL_messages_sendMessage reqSend = new TLRPC.TL_messages_sendMessage();
|
||||
reqSend.message = message;
|
||||
reqSend.clear_draft = retryMessageObject == null;
|
||||
if (newMsg.to_id instanceof TLRPC.TL_peerChannel) {
|
||||
reqSend.silent = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE).getBoolean("silent_" + peer, false);
|
||||
}
|
||||
reqSend.peer = sendToPeer;
|
||||
reqSend.random_id = newMsg.random_id;
|
||||
if (asAdmin && sendToPeer instanceof TLRPC.TL_inputPeerChannel) {
|
||||
reqSend.broadcast = true;
|
||||
}
|
||||
if (reply_to_msg != null) {
|
||||
reqSend.flags |= 1;
|
||||
reqSend.reply_to_msg_id = reply_to_msg.getId();
|
||||
|
@ -1486,6 +1490,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
reqSend.flags |= 8;
|
||||
}
|
||||
performSendMessageRequest(reqSend, newMsgObj, null);
|
||||
if (retryMessageObject == null) {
|
||||
DraftQuery.cleanDraft(peer, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TLRPC.TL_decryptedMessage reqSend;
|
||||
|
@ -1522,6 +1529,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
reqSend.media = new TLRPC.TL_decryptedMessageMediaEmpty();
|
||||
}
|
||||
SecretChatHelper.getInstance().performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, null, null, newMsgObj);
|
||||
if (retryMessageObject == null) {
|
||||
DraftQuery.cleanDraft(peer, false);
|
||||
}
|
||||
}
|
||||
} else if (type >= 1 && type <= 3 || type >= 5 && type <= 8 || type == 9 && encryptedChat != null) {
|
||||
if (encryptedChat == null) {
|
||||
|
@ -1660,6 +1670,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
delayedMessage.sendRequest = request;
|
||||
}
|
||||
reqSend = request;
|
||||
if (retryMessageObject == null) {
|
||||
DraftQuery.cleanDraft(peer, false);
|
||||
}
|
||||
} else {
|
||||
TLRPC.TL_messages_sendMedia request = new TLRPC.TL_messages_sendMedia();
|
||||
request.peer = sendToPeer;
|
||||
|
@ -1668,9 +1681,6 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
}
|
||||
request.random_id = newMsg.random_id;
|
||||
request.media = inputMedia;
|
||||
if (asAdmin && sendToPeer instanceof TLRPC.TL_inputPeerChannel) {
|
||||
request.broadcast = true;
|
||||
}
|
||||
if (reply_to_msg != null) {
|
||||
request.flags |= 1;
|
||||
request.reply_to_msg_id = reply_to_msg.getId();
|
||||
|
@ -1971,6 +1981,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
}
|
||||
performSendDelayedMessage(delayedMessage);
|
||||
}
|
||||
if (retryMessageObject == null) {
|
||||
DraftQuery.cleanDraft(peer, false);
|
||||
}
|
||||
}
|
||||
} else if (type == 4) {
|
||||
TLRPC.TL_messages_forwardMessages reqSend = new TLRPC.TL_messages_forwardMessages();
|
||||
|
@ -1994,16 +2007,10 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
} else {
|
||||
reqSend.id.add(retryMessageObject.messageOwner.fwd_msg_id);
|
||||
}
|
||||
if (asAdmin && reqSend.to_peer.channel_id != 0) {
|
||||
reqSend.broadcast = true;
|
||||
}
|
||||
performSendMessageRequest(reqSend, newMsgObj, null);
|
||||
} else if (type == 9) {
|
||||
TLRPC.TL_messages_sendInlineBotResult reqSend = new TLRPC.TL_messages_sendInlineBotResult();
|
||||
reqSend.peer = sendToPeer;
|
||||
if (asAdmin && sendToPeer instanceof TLRPC.TL_inputPeerChannel) {
|
||||
reqSend.broadcast = true;
|
||||
}
|
||||
reqSend.random_id = newMsg.random_id;
|
||||
if (reply_to_msg != null) {
|
||||
reqSend.flags |= 1;
|
||||
|
@ -2014,6 +2021,10 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
}
|
||||
reqSend.query_id = Utilities.parseLong(params.get("query_id"));
|
||||
reqSend.id = params.get("id");
|
||||
if (retryMessageObject == null) {
|
||||
reqSend.clear_draft = true;
|
||||
DraftQuery.cleanDraft(peer, false);
|
||||
}
|
||||
performSendMessageRequest(reqSend, newMsgObj, null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -2239,6 +2250,13 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
}
|
||||
}
|
||||
if (message != null) {
|
||||
Integer value = MessagesController.getInstance().dialogs_read_outbox_max.get(message.dialog_id);
|
||||
if (value == null) {
|
||||
value = MessagesStorage.getInstance().getDialogReadMax(message.out, message.dialog_id);
|
||||
MessagesController.getInstance().dialogs_read_outbox_max.put(message.dialog_id, value);
|
||||
}
|
||||
message.unread = value < message.id;
|
||||
|
||||
newMsgObj.id = message.id;
|
||||
updateMediaPaths(msgObj, message, originalPath, false);
|
||||
} else {
|
||||
|
@ -2518,7 +2536,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
}
|
||||
}
|
||||
|
||||
private static boolean prepareSendingDocumentInternal(String path, String originalPath, Uri uri, String mime, final long dialog_id, final MessageObject reply_to_msg, final boolean asAdmin, String caption) {
|
||||
private static boolean prepareSendingDocumentInternal(String path, String originalPath, Uri uri, String mime, final long dialog_id, final MessageObject reply_to_msg, String caption) {
|
||||
if ((path == null || path.length() == 0) && uri == null) {
|
||||
return false;
|
||||
}
|
||||
|
@ -2678,13 +2696,13 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SendMessagesHelper.getInstance().sendMessage(documentFinal, null, pathFinal, dialog_id, reply_to_msg, asAdmin, null, params);
|
||||
SendMessagesHelper.getInstance().sendMessage(documentFinal, null, pathFinal, dialog_id, reply_to_msg, null, params);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void prepareSendingDocument(String path, String originalPath, Uri uri, String mine, long dialog_id, MessageObject reply_to_msg, boolean asAdmin) {
|
||||
public static void prepareSendingDocument(String path, String originalPath, Uri uri, String mine, long dialog_id, MessageObject reply_to_msg) {
|
||||
if ((path == null || originalPath == null) && uri == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -2696,10 +2714,10 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
}
|
||||
paths.add(path);
|
||||
originalPaths.add(originalPath);
|
||||
prepareSendingDocuments(paths, originalPaths, uris, mine, dialog_id, reply_to_msg, asAdmin);
|
||||
prepareSendingDocuments(paths, originalPaths, uris, mine, dialog_id, reply_to_msg);
|
||||
}
|
||||
|
||||
public static void prepareSendingAudioDocuments(final ArrayList<MessageObject> messageObjects, final long dialog_id, final MessageObject reply_to_msg, final boolean asAdmin) {
|
||||
public static void prepareSendingAudioDocuments(final ArrayList<MessageObject> messageObjects, final long dialog_id, final MessageObject reply_to_msg) {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -2751,7 +2769,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SendMessagesHelper.getInstance().sendMessage(documentFinal, null, messageObject.messageOwner.attachPath, dialog_id, reply_to_msg, asAdmin, null, params);
|
||||
SendMessagesHelper.getInstance().sendMessage(documentFinal, null, messageObject.messageOwner.attachPath, dialog_id, reply_to_msg, null, params);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -2759,7 +2777,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
}).start();
|
||||
}
|
||||
|
||||
public static void prepareSendingDocuments(final ArrayList<String> paths, final ArrayList<String> originalPaths, final ArrayList<Uri> uris, final String mime, final long dialog_id, final MessageObject reply_to_msg, final boolean asAdmin) {
|
||||
public static void prepareSendingDocuments(final ArrayList<String> paths, final ArrayList<String> originalPaths, final ArrayList<Uri> uris, final String mime, final long dialog_id, final MessageObject reply_to_msg) {
|
||||
if (paths == null && originalPaths == null && uris == null || paths != null && originalPaths != null && paths.size() != originalPaths.size()) {
|
||||
return;
|
||||
}
|
||||
|
@ -2769,14 +2787,14 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
boolean error = false;
|
||||
if (paths != null) {
|
||||
for (int a = 0; a < paths.size(); a++) {
|
||||
if (!prepareSendingDocumentInternal(paths.get(a), originalPaths.get(a), null, mime, dialog_id, reply_to_msg, asAdmin, null)) {
|
||||
if (!prepareSendingDocumentInternal(paths.get(a), originalPaths.get(a), null, mime, dialog_id, reply_to_msg, null)) {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uris != null) {
|
||||
for (int a = 0; a < uris.size(); a++) {
|
||||
if (!prepareSendingDocumentInternal(null, null, uris.get(a), mime, dialog_id, reply_to_msg, asAdmin, null)) {
|
||||
if (!prepareSendingDocumentInternal(null, null, uris.get(a), mime, dialog_id, reply_to_msg, null)) {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
@ -2798,7 +2816,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
}).start();
|
||||
}
|
||||
|
||||
public static void prepareSendingPhoto(String imageFilePath, Uri imageUri, long dialog_id, MessageObject reply_to_msg, CharSequence caption, boolean asAdmin) {
|
||||
public static void prepareSendingPhoto(String imageFilePath, Uri imageUri, long dialog_id, MessageObject reply_to_msg, CharSequence caption) {
|
||||
ArrayList<String> paths = null;
|
||||
ArrayList<Uri> uris = null;
|
||||
ArrayList<String> captions = null;
|
||||
|
@ -2814,10 +2832,10 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
captions = new ArrayList<>();
|
||||
captions.add(caption.toString());
|
||||
}
|
||||
prepareSendingPhotos(paths, uris, dialog_id, reply_to_msg, captions, asAdmin);
|
||||
prepareSendingPhotos(paths, uris, dialog_id, reply_to_msg, captions);
|
||||
}
|
||||
|
||||
public static void prepareSendingBotContextResult(final TLRPC.BotInlineResult result, final HashMap<String, String> params, final long dialog_id, final MessageObject reply_to_msg, final boolean asAdmin) {
|
||||
public static void prepareSendingBotContextResult(final TLRPC.BotInlineResult result, final HashMap<String, String> params, final long dialog_id, final MessageObject reply_to_msg) {
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -3014,17 +3032,17 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
public void run() {
|
||||
if (finalDocument != null) {
|
||||
finalDocument.caption = result.send_message.caption;
|
||||
SendMessagesHelper.getInstance().sendMessage(finalDocument, null, finalPathFinal, dialog_id, reply_to_msg, asAdmin, result.send_message.reply_markup, params);
|
||||
SendMessagesHelper.getInstance().sendMessage(finalDocument, null, finalPathFinal, dialog_id, reply_to_msg, result.send_message.reply_markup, params);
|
||||
} else if (finalPhoto != null) {
|
||||
finalPhoto.caption = result.send_message.caption;
|
||||
SendMessagesHelper.getInstance().sendMessage(finalPhoto, result.content_url, dialog_id, reply_to_msg, asAdmin, result.send_message.reply_markup, params);
|
||||
SendMessagesHelper.getInstance().sendMessage(finalPhoto, result.content_url, dialog_id, reply_to_msg, result.send_message.reply_markup, params);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}).run();
|
||||
} else if (result.send_message instanceof TLRPC.TL_botInlineMessageText) {
|
||||
SendMessagesHelper.getInstance().sendMessage(result.send_message.message, dialog_id, reply_to_msg, null, !result.send_message.no_webpage, asAdmin, result.send_message.entities, result.send_message.reply_markup, params);
|
||||
SendMessagesHelper.getInstance().sendMessage(result.send_message.message, dialog_id, reply_to_msg, null, !result.send_message.no_webpage, result.send_message.entities, result.send_message.reply_markup, params);
|
||||
} else if (result.send_message instanceof TLRPC.TL_botInlineMessageMediaVenue) {
|
||||
TLRPC.TL_messageMediaVenue venue = new TLRPC.TL_messageMediaVenue();
|
||||
venue.geo = result.send_message.geo;
|
||||
|
@ -3032,21 +3050,21 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
venue.title = result.send_message.title;
|
||||
venue.provider = result.send_message.provider;
|
||||
venue.venue_id = result.send_message.venue_id;
|
||||
SendMessagesHelper.getInstance().sendMessage(venue, dialog_id, reply_to_msg, asAdmin, result.send_message.reply_markup, params);
|
||||
SendMessagesHelper.getInstance().sendMessage(venue, dialog_id, reply_to_msg, result.send_message.reply_markup, params);
|
||||
} else if (result.send_message instanceof TLRPC.TL_botInlineMessageMediaGeo) {
|
||||
TLRPC.TL_messageMediaGeo location = new TLRPC.TL_messageMediaGeo();
|
||||
location.geo = result.send_message.geo;
|
||||
SendMessagesHelper.getInstance().sendMessage(location, dialog_id, reply_to_msg, asAdmin, result.send_message.reply_markup, params);
|
||||
SendMessagesHelper.getInstance().sendMessage(location, dialog_id, reply_to_msg, result.send_message.reply_markup, params);
|
||||
} else if (result.send_message instanceof TLRPC.TL_botInlineMessageMediaContact) {
|
||||
TLRPC.User user = new TLRPC.TL_user();
|
||||
user.phone = result.send_message.phone_number;
|
||||
user.first_name = result.send_message.first_name;
|
||||
user.last_name = result.send_message.last_name;
|
||||
SendMessagesHelper.getInstance().sendMessage(user, dialog_id, reply_to_msg, asAdmin, result.send_message.reply_markup, params);
|
||||
SendMessagesHelper.getInstance().sendMessage(user, dialog_id, reply_to_msg, result.send_message.reply_markup, params);
|
||||
}
|
||||
}
|
||||
|
||||
public static void prepareSendingPhotosSearch(final ArrayList<MediaController.SearchImage> photos, final long dialog_id, final MessageObject reply_to_msg, final boolean asAdmin) {
|
||||
public static void prepareSendingPhotosSearch(final ArrayList<MediaController.SearchImage> photos, final long dialog_id, final MessageObject reply_to_msg) {
|
||||
if (photos == null || photos.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -3142,7 +3160,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SendMessagesHelper.getInstance().sendMessage(documentFinal, null, pathFinal, dialog_id, reply_to_msg, asAdmin, null, params);
|
||||
SendMessagesHelper.getInstance().sendMessage(documentFinal, null, pathFinal, dialog_id, reply_to_msg, null, params);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
@ -3192,7 +3210,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SendMessagesHelper.getInstance().sendMessage(photoFinal, needDownloadHttpFinal ? searchImage.imageUrl : null, dialog_id, reply_to_msg, asAdmin, null, params);
|
||||
SendMessagesHelper.getInstance().sendMessage(photoFinal, needDownloadHttpFinal ? searchImage.imageUrl : null, dialog_id, reply_to_msg, null, params);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -3216,7 +3234,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
return src;
|
||||
}
|
||||
|
||||
public static void prepareSendingText(final String text, final long dialog_id, final boolean asAdmin) {
|
||||
public static void prepareSendingText(final String text, final long dialog_id) {
|
||||
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -3231,7 +3249,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
int count = (int) Math.ceil(textFinal.length() / 4096.0f);
|
||||
for (int a = 0; a < count; a++) {
|
||||
String mess = textFinal.substring(a * 4096, Math.min((a + 1) * 4096, textFinal.length()));
|
||||
SendMessagesHelper.getInstance().sendMessage(mess, dialog_id, null, null, true, asAdmin, null, null, null);
|
||||
SendMessagesHelper.getInstance().sendMessage(mess, dialog_id, null, null, true, null, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3242,7 +3260,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
});
|
||||
}
|
||||
|
||||
public static void prepareSendingPhotos(ArrayList<String> paths, ArrayList<Uri> uris, final long dialog_id, final MessageObject reply_to_msg, final ArrayList<String> captions, final boolean asAdmin) {
|
||||
public static void prepareSendingPhotos(ArrayList<String> paths, ArrayList<Uri> uris, final long dialog_id, final MessageObject reply_to_msg, final ArrayList<String> captions) {
|
||||
if (paths == null && uris == null || paths != null && paths.isEmpty() || uris != null && uris.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -3340,7 +3358,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SendMessagesHelper.getInstance().sendMessage(photoFinal, null, dialog_id, reply_to_msg, asAdmin, null, params);
|
||||
SendMessagesHelper.getInstance().sendMessage(photoFinal, null, dialog_id, reply_to_msg, null, params);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -3348,14 +3366,14 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
}
|
||||
if (sendAsDocuments != null && !sendAsDocuments.isEmpty()) {
|
||||
for (int a = 0; a < sendAsDocuments.size(); a++) {
|
||||
prepareSendingDocumentInternal(sendAsDocuments.get(a), sendAsDocumentsOriginal.get(a), null, extension, dialog_id, reply_to_msg, asAdmin, sendAsDocumentsCaptions.get(a));
|
||||
prepareSendingDocumentInternal(sendAsDocuments.get(a), sendAsDocumentsOriginal.get(a), null, extension, dialog_id, reply_to_msg, sendAsDocumentsCaptions.get(a));
|
||||
}
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static void prepareSendingVideo(final String videoPath, final long estimatedSize, final long duration, final int width, final int height, final VideoEditedInfo videoEditedInfo, final long dialog_id, final MessageObject reply_to_msg, final boolean asAdmin) {
|
||||
public static void prepareSendingVideo(final String videoPath, final long estimatedSize, final long duration, final int width, final int height, final VideoEditedInfo videoEditedInfo, final long dialog_id, final MessageObject reply_to_msg) {
|
||||
if (videoPath == null || videoPath.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -3415,34 +3433,33 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
document.size = (int) temp.length();
|
||||
}
|
||||
boolean infoObtained = false;
|
||||
if (Build.VERSION.SDK_INT >= 14) {
|
||||
MediaMetadataRetriever mediaMetadataRetriever = null;
|
||||
|
||||
MediaMetadataRetriever mediaMetadataRetriever = null;
|
||||
try {
|
||||
mediaMetadataRetriever = new MediaMetadataRetriever();
|
||||
mediaMetadataRetriever.setDataSource(videoPath);
|
||||
String width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
|
||||
if (width != null) {
|
||||
attributeVideo.w = Integer.parseInt(width);
|
||||
}
|
||||
String height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
|
||||
if (height != null) {
|
||||
attributeVideo.h = Integer.parseInt(height);
|
||||
}
|
||||
String duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
|
||||
if (duration != null) {
|
||||
attributeVideo.duration = (int) Math.ceil(Long.parseLong(duration) / 1000.0f);
|
||||
}
|
||||
infoObtained = true;
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
} finally {
|
||||
try {
|
||||
mediaMetadataRetriever = new MediaMetadataRetriever();
|
||||
mediaMetadataRetriever.setDataSource(videoPath);
|
||||
String width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
|
||||
if (width != null) {
|
||||
attributeVideo.w = Integer.parseInt(width);
|
||||
if (mediaMetadataRetriever != null) {
|
||||
mediaMetadataRetriever.release();
|
||||
}
|
||||
String height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
|
||||
if (height != null) {
|
||||
attributeVideo.h = Integer.parseInt(height);
|
||||
}
|
||||
String duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
|
||||
if (duration != null) {
|
||||
attributeVideo.duration = (int) Math.ceil(Long.parseLong(duration) / 1000.0f);
|
||||
}
|
||||
infoObtained = true;
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
} finally {
|
||||
try {
|
||||
if (mediaMetadataRetriever != null) {
|
||||
mediaMetadataRetriever.release();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!infoObtained) {
|
||||
|
@ -3470,11 +3487,11 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SendMessagesHelper.getInstance().sendMessage(videoFinal, videoEditedInfo, finalPath, dialog_id, reply_to_msg, asAdmin, null, params);
|
||||
SendMessagesHelper.getInstance().sendMessage(videoFinal, videoEditedInfo, finalPath, dialog_id, reply_to_msg, null, params);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
prepareSendingDocumentInternal(videoPath, videoPath, null, null, dialog_id, reply_to_msg, asAdmin, null);
|
||||
prepareSendingDocumentInternal(videoPath, videoPath, null, null, dialog_id, reply_to_msg, null);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
|
|
@ -20,28 +20,27 @@ import java.io.File;
|
|||
public class UserConfig {
|
||||
|
||||
private static TLRPC.User currentUser;
|
||||
public static boolean registeredForPush = false;
|
||||
public static boolean registeredForPush;
|
||||
public static String pushString = "";
|
||||
public static int lastSendMessageId = -210000;
|
||||
public static int lastLocalId = -210000;
|
||||
public static int lastBroadcastId = -1;
|
||||
public static String contactsHash = "";
|
||||
public static String importHash = "";
|
||||
public static boolean blockedUsersLoaded = false;
|
||||
public static boolean blockedUsersLoaded;
|
||||
private final static Object sync = new Object();
|
||||
public static boolean saveIncomingPhotos = false;
|
||||
public static int contactsVersion = 1;
|
||||
public static boolean saveIncomingPhotos;
|
||||
public static String passcodeHash = "";
|
||||
public static byte[] passcodeSalt = new byte[0];
|
||||
public static boolean appLocked = false;
|
||||
public static int passcodeType = 0;
|
||||
public static boolean appLocked;
|
||||
public static int passcodeType;
|
||||
public static int autoLockIn = 60 * 60;
|
||||
public static int lastPauseTime = 0;
|
||||
public static boolean isWaitingForPasscodeEnter = false;
|
||||
public static int lastPauseTime;
|
||||
public static boolean isWaitingForPasscodeEnter;
|
||||
public static boolean useFingerprint = true;
|
||||
public static String lastUpdateVersion;
|
||||
public static int lastContactsSyncTime;
|
||||
public static int lastHintsSyncTime;
|
||||
public static boolean draftsLoaded;
|
||||
|
||||
public static int migrateOffsetId = -1;
|
||||
public static int migrateOffsetDate = -1;
|
||||
|
@ -73,9 +72,7 @@ public class UserConfig {
|
|||
editor.putInt("lastSendMessageId", lastSendMessageId);
|
||||
editor.putInt("lastLocalId", lastLocalId);
|
||||
editor.putString("contactsHash", contactsHash);
|
||||
editor.putString("importHash", importHash);
|
||||
editor.putBoolean("saveIncomingPhotos", saveIncomingPhotos);
|
||||
editor.putInt("contactsVersion", contactsVersion);
|
||||
editor.putInt("lastBroadcastId", lastBroadcastId);
|
||||
editor.putBoolean("blockedUsersLoaded", blockedUsersLoaded);
|
||||
editor.putString("passcodeHash1", passcodeHash);
|
||||
|
@ -88,6 +85,7 @@ public class UserConfig {
|
|||
editor.putInt("lastContactsSyncTime", lastContactsSyncTime);
|
||||
editor.putBoolean("useFingerprint", useFingerprint);
|
||||
editor.putInt("lastHintsSyncTime", lastHintsSyncTime);
|
||||
editor.putBoolean("draftsLoaded", draftsLoaded);
|
||||
|
||||
editor.putInt("migrateOffsetId", migrateOffsetId);
|
||||
if (migrateOffsetId != -1) {
|
||||
|
@ -162,9 +160,8 @@ public class UserConfig {
|
|||
lastSendMessageId = data.readInt32(false);
|
||||
lastLocalId = data.readInt32(false);
|
||||
contactsHash = data.readString(false);
|
||||
importHash = data.readString(false);
|
||||
data.readString(false);
|
||||
saveIncomingPhotos = data.readBool(false);
|
||||
contactsVersion = 0;
|
||||
MessagesStorage.lastQtsValue = data.readInt32(false);
|
||||
MessagesStorage.lastSecretVersion = data.readInt32(false);
|
||||
int val = data.readInt32(false);
|
||||
|
@ -188,9 +185,7 @@ public class UserConfig {
|
|||
lastSendMessageId = preferences.getInt("lastSendMessageId", -210000);
|
||||
lastLocalId = preferences.getInt("lastLocalId", -210000);
|
||||
contactsHash = preferences.getString("contactsHash", "");
|
||||
importHash = preferences.getString("importHash", "");
|
||||
saveIncomingPhotos = preferences.getBoolean("saveIncomingPhotos", false);
|
||||
contactsVersion = preferences.getInt("contactsVersion", 0);
|
||||
}
|
||||
if (lastLocalId > -210000) {
|
||||
lastLocalId = -210000;
|
||||
|
@ -215,9 +210,7 @@ public class UserConfig {
|
|||
lastSendMessageId = preferences.getInt("lastSendMessageId", -210000);
|
||||
lastLocalId = preferences.getInt("lastLocalId", -210000);
|
||||
contactsHash = preferences.getString("contactsHash", "");
|
||||
importHash = preferences.getString("importHash", "");
|
||||
saveIncomingPhotos = preferences.getBoolean("saveIncomingPhotos", false);
|
||||
contactsVersion = preferences.getInt("contactsVersion", 0);
|
||||
lastBroadcastId = preferences.getInt("lastBroadcastId", -1);
|
||||
blockedUsersLoaded = preferences.getBoolean("blockedUsersLoaded", false);
|
||||
passcodeHash = preferences.getString("passcodeHash1", "");
|
||||
|
@ -229,6 +222,7 @@ public class UserConfig {
|
|||
lastUpdateVersion = preferences.getString("lastUpdateVersion2", "3.5");
|
||||
lastContactsSyncTime = preferences.getInt("lastContactsSyncTime", (int) (System.currentTimeMillis() / 1000) - 23 * 60 * 60);
|
||||
lastHintsSyncTime = preferences.getInt("lastHintsSyncTime", (int) (System.currentTimeMillis() / 1000) - 25 * 60 * 60);
|
||||
draftsLoaded = preferences.getBoolean("draftsLoaded", false);
|
||||
|
||||
migrateOffsetId = preferences.getInt("migrateOffsetId", 0);
|
||||
if (migrateOffsetId != -1) {
|
||||
|
@ -297,9 +291,7 @@ public class UserConfig {
|
|||
currentUser = null;
|
||||
registeredForPush = false;
|
||||
contactsHash = "";
|
||||
importHash = "";
|
||||
lastSendMessageId = -210000;
|
||||
contactsVersion = 1;
|
||||
lastBroadcastId = -1;
|
||||
saveIncomingPhotos = false;
|
||||
blockedUsersLoaded = false;
|
||||
|
@ -316,6 +308,7 @@ public class UserConfig {
|
|||
autoLockIn = 60 * 60;
|
||||
lastPauseTime = 0;
|
||||
useFingerprint = true;
|
||||
draftsLoaded = true;
|
||||
isWaitingForPasscodeEnter = false;
|
||||
lastUpdateVersion = BuildVars.BUILD_VERSION_STRING;
|
||||
lastContactsSyncTime = (int) (System.currentTimeMillis() / 1000) - 23 * 60 * 60;
|
||||
|
|
|
@ -22,7 +22,7 @@ import java.util.regex.Pattern;
|
|||
|
||||
public class Utilities {
|
||||
|
||||
public static Pattern pattern = Pattern.compile("[0-9]+");
|
||||
public static Pattern pattern = Pattern.compile("[\\-0-9]+");
|
||||
public static SecureRandom random = new SecureRandom();
|
||||
|
||||
public static volatile DispatchQueue stageQueue = new DispatchQueue("stageQueue");
|
||||
|
|
|
@ -25,27 +25,33 @@ public class VideoEditedInfo {
|
|||
return String.format(Locale.US, "-1_%d_%d_%d_%d_%d_%d_%d_%d_%s", startTime, endTime, rotationValue, originalWidth, originalHeight, bitrate, resultWidth, resultHeight, originalPath);
|
||||
}
|
||||
|
||||
public void parseString(String string) {
|
||||
public boolean parseString(String string) {
|
||||
if (string.length() < 6) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
String args[] = string.split("_");
|
||||
if (args.length >= 10) {
|
||||
startTime = Long.parseLong(args[1]);
|
||||
endTime = Long.parseLong(args[2]);
|
||||
rotationValue = Integer.parseInt(args[3]);
|
||||
originalWidth = Integer.parseInt(args[4]);
|
||||
originalHeight = Integer.parseInt(args[5]);
|
||||
bitrate = Integer.parseInt(args[6]);
|
||||
resultWidth = Integer.parseInt(args[7]);
|
||||
resultHeight = Integer.parseInt(args[8]);
|
||||
for (int a = 9; a < args.length; a++) {
|
||||
if (originalPath == null) {
|
||||
originalPath = args[a];
|
||||
} else {
|
||||
originalPath += "_" + args[a];
|
||||
try {
|
||||
String args[] = string.split("_");
|
||||
if (args.length >= 10) {
|
||||
startTime = Long.parseLong(args[1]);
|
||||
endTime = Long.parseLong(args[2]);
|
||||
rotationValue = Integer.parseInt(args[3]);
|
||||
originalWidth = Integer.parseInt(args[4]);
|
||||
originalHeight = Integer.parseInt(args[5]);
|
||||
bitrate = Integer.parseInt(args[6]);
|
||||
resultWidth = Integer.parseInt(args[7]);
|
||||
resultHeight = Integer.parseInt(args[8]);
|
||||
for (int a = 9; a < args.length; a++) {
|
||||
if (originalPath == null) {
|
||||
originalPath = args[a];
|
||||
} else {
|
||||
originalPath += "_" + args[a];
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ public class WearReplyReceiver extends BroadcastReceiver {
|
|||
if (dialog_id == 0 || max_id == 0) {
|
||||
return;
|
||||
}
|
||||
SendMessagesHelper.getInstance().sendMessage(text.toString(), dialog_id, null, null, true, false, null, null, null);
|
||||
SendMessagesHelper.getInstance().sendMessage(text.toString(), dialog_id, null, null, true, null, null, null);
|
||||
MessagesController.getInstance().markDialogAsRead(dialog_id, max_id, max_id, 0, true, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,11 +88,13 @@ public class Browser {
|
|||
@Override
|
||||
public void onServiceConnected(CustomTabsClient client) {
|
||||
customTabsClient = client;
|
||||
if (customTabsClient != null) {
|
||||
try {
|
||||
customTabsClient.warmup(0);
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
if (MediaController.getInstance().canCustomTabs()) {
|
||||
if (customTabsClient != null) {
|
||||
try {
|
||||
customTabsClient.warmup(0);
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
/**
|
||||
* A {@link FrameLayout} that resizes itself to match a specified aspect ratio.
|
||||
*/
|
||||
public final class AspectRatioFrameLayout extends FrameLayout {
|
||||
|
||||
/**
|
||||
* The {@link FrameLayout} will not resize itself if the fractional difference between its natural
|
||||
* aspect ratio and the requested aspect ratio falls below this threshold.
|
||||
* <p>
|
||||
* This tolerance allows the view to occupy the whole of the screen when the requested aspect
|
||||
* ratio is very close, but not exactly equal to, the aspect ratio of the screen. This may reduce
|
||||
* the number of view layers that need to be composited by the underlying system, which can help
|
||||
* to reduce power consumption.
|
||||
*/
|
||||
private static final float MAX_ASPECT_RATIO_DEFORMATION_FRACTION = 0.01f;
|
||||
|
||||
private float videoAspectRatio;
|
||||
|
||||
public AspectRatioFrameLayout(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public AspectRatioFrameLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the aspect ratio that this view should satisfy.
|
||||
*
|
||||
* @param widthHeightRatio The width to height ratio.
|
||||
*/
|
||||
public void setAspectRatio(float widthHeightRatio) {
|
||||
if (this.videoAspectRatio != widthHeightRatio) {
|
||||
this.videoAspectRatio = widthHeightRatio;
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
if (videoAspectRatio == 0) {
|
||||
// Aspect ratio not set.
|
||||
return;
|
||||
}
|
||||
|
||||
int width = getMeasuredWidth();
|
||||
int height = getMeasuredHeight();
|
||||
float viewAspectRatio = (float) width / height;
|
||||
float aspectDeformation = videoAspectRatio / viewAspectRatio - 1;
|
||||
if (Math.abs(aspectDeformation) <= MAX_ASPECT_RATIO_DEFORMATION_FRACTION) {
|
||||
// We're within the allowed tolerance.
|
||||
return;
|
||||
}
|
||||
|
||||
if (aspectDeformation > 0) {
|
||||
height = (int) (width / videoAspectRatio);
|
||||
} else {
|
||||
width = (int) (height * videoAspectRatio);
|
||||
}
|
||||
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Thrown when a live playback falls behind the available media window.
|
||||
*/
|
||||
public final class BehindLiveWindowException extends IOException {
|
||||
|
||||
public BehindLiveWindowException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public BehindLiveWindowException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
146
TMessagesProj/src/main/java/org/telegram/messenger/exoplayer/C.java
Executable file
146
TMessagesProj/src/main/java/org/telegram/messenger/exoplayer/C.java
Executable file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import org.telegram.messenger.exoplayer.util.Util;
|
||||
|
||||
import android.media.AudioFormat;
|
||||
import android.media.MediaCodec;
|
||||
import android.media.MediaExtractor;
|
||||
|
||||
/**
|
||||
* Defines constants that are generally useful throughout the library.
|
||||
*/
|
||||
public final class C {
|
||||
|
||||
/**
|
||||
* Represents an unknown microsecond time or duration.
|
||||
*/
|
||||
public static final long UNKNOWN_TIME_US = -1L;
|
||||
|
||||
/**
|
||||
* Represents a microsecond duration whose exact value is unknown, but which should match the
|
||||
* longest of some other known durations.
|
||||
*/
|
||||
public static final long MATCH_LONGEST_US = -2L;
|
||||
|
||||
/**
|
||||
* The number of microseconds in one second.
|
||||
*/
|
||||
public static final long MICROS_PER_SECOND = 1000000L;
|
||||
|
||||
/**
|
||||
* Represents an unbounded length of data.
|
||||
*/
|
||||
public static final int LENGTH_UNBOUNDED = -1;
|
||||
|
||||
/**
|
||||
* The name of the UTF-8 charset.
|
||||
*/
|
||||
public static final String UTF8_NAME = "UTF-8";
|
||||
|
||||
/**
|
||||
* @see MediaCodec#CRYPTO_MODE_AES_CTR
|
||||
*/
|
||||
@SuppressWarnings("InlinedApi")
|
||||
public static final int CRYPTO_MODE_AES_CTR = MediaCodec.CRYPTO_MODE_AES_CTR;
|
||||
|
||||
/**
|
||||
* @see AudioFormat#ENCODING_INVALID
|
||||
*/
|
||||
public static final int ENCODING_INVALID = AudioFormat.ENCODING_INVALID;
|
||||
|
||||
/**
|
||||
* @see AudioFormat#ENCODING_PCM_8BIT
|
||||
*/
|
||||
public static final int ENCODING_PCM_8BIT = AudioFormat.ENCODING_PCM_8BIT;
|
||||
|
||||
/**
|
||||
* @see AudioFormat#ENCODING_PCM_16BIT
|
||||
*/
|
||||
public static final int ENCODING_PCM_16BIT = AudioFormat.ENCODING_PCM_16BIT;
|
||||
|
||||
/**
|
||||
* PCM encoding with 24 bits per sample.
|
||||
*/
|
||||
public static final int ENCODING_PCM_24BIT = 0x80000000;
|
||||
|
||||
/**
|
||||
* PCM encoding with 32 bits per sample.
|
||||
*/
|
||||
public static final int ENCODING_PCM_32BIT = 0x40000000;
|
||||
|
||||
/**
|
||||
* @see AudioFormat#ENCODING_AC3
|
||||
*/
|
||||
@SuppressWarnings("InlinedApi")
|
||||
public static final int ENCODING_AC3 = AudioFormat.ENCODING_AC3;
|
||||
|
||||
/**
|
||||
* @see AudioFormat#ENCODING_E_AC3
|
||||
*/
|
||||
@SuppressWarnings("InlinedApi")
|
||||
public static final int ENCODING_E_AC3 = AudioFormat.ENCODING_E_AC3;
|
||||
|
||||
/**
|
||||
* @see AudioFormat#ENCODING_DTS
|
||||
*/
|
||||
@SuppressWarnings("InlinedApi")
|
||||
public static final int ENCODING_DTS = AudioFormat.ENCODING_DTS;
|
||||
|
||||
/**
|
||||
* @see AudioFormat#ENCODING_DTS_HD
|
||||
*/
|
||||
@SuppressWarnings("InlinedApi")
|
||||
public static final int ENCODING_DTS_HD = AudioFormat.ENCODING_DTS_HD;
|
||||
|
||||
/**
|
||||
* @see AudioFormat#CHANNEL_OUT_7POINT1_SURROUND
|
||||
*/
|
||||
@SuppressWarnings({"InlinedApi", "deprecation"})
|
||||
public static final int CHANNEL_OUT_7POINT1_SURROUND = Util.SDK_INT < 23
|
||||
? AudioFormat.CHANNEL_OUT_7POINT1 : AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
|
||||
|
||||
/**
|
||||
* @see MediaExtractor#SAMPLE_FLAG_SYNC
|
||||
*/
|
||||
@SuppressWarnings("InlinedApi")
|
||||
public static final int SAMPLE_FLAG_SYNC = MediaExtractor.SAMPLE_FLAG_SYNC;
|
||||
|
||||
/**
|
||||
* @see MediaExtractor#SAMPLE_FLAG_ENCRYPTED
|
||||
*/
|
||||
@SuppressWarnings("InlinedApi")
|
||||
public static final int SAMPLE_FLAG_ENCRYPTED = MediaExtractor.SAMPLE_FLAG_ENCRYPTED;
|
||||
|
||||
/**
|
||||
* Indicates that a sample should be decoded but not rendered.
|
||||
*/
|
||||
public static final int SAMPLE_FLAG_DECODE_ONLY = 0x8000000;
|
||||
|
||||
/**
|
||||
* A return value for methods where the end of an input was encountered.
|
||||
*/
|
||||
public static final int RESULT_END_OF_INPUT = -1;
|
||||
|
||||
/**
|
||||
* A return value for methods where the length of parsed data exceeds the maximum length allowed.
|
||||
*/
|
||||
public static final int RESULT_MAX_LENGTH_EXCEEDED = -2;
|
||||
|
||||
private C() {}
|
||||
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
/**
|
||||
* Maintains codec event counts, for debugging purposes only.
|
||||
* <p>
|
||||
* Counters should be written from the playback thread only. Counters may be read from any thread.
|
||||
* To ensure that the counter values are correctly reflected between threads, users of this class
|
||||
* should invoke {@link #ensureUpdated()} prior to reading and after writing.
|
||||
*/
|
||||
public final class CodecCounters {
|
||||
|
||||
public int codecInitCount;
|
||||
public int codecReleaseCount;
|
||||
public int inputBufferCount;
|
||||
public int outputFormatChangedCount;
|
||||
public int outputBuffersChangedCount;
|
||||
public int renderedOutputBufferCount;
|
||||
public int skippedOutputBufferCount;
|
||||
public int droppedOutputBufferCount;
|
||||
public int maxConsecutiveDroppedOutputBufferCount;
|
||||
|
||||
/**
|
||||
* Should be invoked from the playback thread after the counters have been updated. Should also
|
||||
* be invoked from any other thread that wishes to read the counters, before reading. These calls
|
||||
* ensure that counter updates are made visible to the reading threads.
|
||||
*/
|
||||
public synchronized void ensureUpdated() {
|
||||
// Do nothing. The use of synchronized ensures a memory barrier should another thread also
|
||||
// call this method.
|
||||
}
|
||||
|
||||
public String getDebugString() {
|
||||
ensureUpdated();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("cic:").append(codecInitCount);
|
||||
builder.append(" crc:").append(codecReleaseCount);
|
||||
builder.append(" ibc:").append(inputBufferCount);
|
||||
builder.append(" ofc:").append(outputFormatChangedCount);
|
||||
builder.append(" obc:").append(outputBuffersChangedCount);
|
||||
builder.append(" ren:").append(renderedOutputBufferCount);
|
||||
builder.append(" sob:").append(skippedOutputBufferCount);
|
||||
builder.append(" dob:").append(droppedOutputBufferCount);
|
||||
builder.append(" mcdob:").append(maxConsecutiveDroppedOutputBufferCount);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
116
TMessagesProj/src/main/java/org/telegram/messenger/exoplayer/CryptoInfo.java
Executable file
116
TMessagesProj/src/main/java/org/telegram/messenger/exoplayer/CryptoInfo.java
Executable file
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import org.telegram.messenger.exoplayer.util.Util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.media.MediaExtractor;
|
||||
|
||||
/**
|
||||
* Compatibility wrapper around {@link android.media.MediaCodec.CryptoInfo}.
|
||||
*/
|
||||
public final class CryptoInfo {
|
||||
|
||||
/**
|
||||
* @see android.media.MediaCodec.CryptoInfo#iv
|
||||
*/
|
||||
public byte[] iv;
|
||||
/**
|
||||
* @see android.media.MediaCodec.CryptoInfo#key
|
||||
*/
|
||||
public byte[] key;
|
||||
/**
|
||||
* @see android.media.MediaCodec.CryptoInfo#mode
|
||||
*/
|
||||
public int mode;
|
||||
/**
|
||||
* @see android.media.MediaCodec.CryptoInfo#numBytesOfClearData
|
||||
*/
|
||||
public int[] numBytesOfClearData;
|
||||
/**
|
||||
* @see android.media.MediaCodec.CryptoInfo#numBytesOfEncryptedData
|
||||
*/
|
||||
public int[] numBytesOfEncryptedData;
|
||||
/**
|
||||
* @see android.media.MediaCodec.CryptoInfo#numSubSamples
|
||||
*/
|
||||
public int numSubSamples;
|
||||
|
||||
private final android.media.MediaCodec.CryptoInfo frameworkCryptoInfo;
|
||||
|
||||
public CryptoInfo() {
|
||||
frameworkCryptoInfo = Util.SDK_INT >= 16 ? newFrameworkCryptoInfoV16() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see android.media.MediaCodec.CryptoInfo#set(int, int[], int[], byte[], byte[], int)
|
||||
*/
|
||||
public void set(int numSubSamples, int[] numBytesOfClearData, int[] numBytesOfEncryptedData,
|
||||
byte[] key, byte[] iv, int mode) {
|
||||
this.numSubSamples = numSubSamples;
|
||||
this.numBytesOfClearData = numBytesOfClearData;
|
||||
this.numBytesOfEncryptedData = numBytesOfEncryptedData;
|
||||
this.key = key;
|
||||
this.iv = iv;
|
||||
this.mode = mode;
|
||||
if (Util.SDK_INT >= 16) {
|
||||
updateFrameworkCryptoInfoV16();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to {@link MediaExtractor#getSampleCryptoInfo(android.media.MediaCodec.CryptoInfo)}.
|
||||
*
|
||||
* @param extractor The extractor from which to retrieve the crypto information.
|
||||
*/
|
||||
@TargetApi(16)
|
||||
public void setFromExtractorV16(MediaExtractor extractor) {
|
||||
extractor.getSampleCryptoInfo(frameworkCryptoInfo);
|
||||
numSubSamples = frameworkCryptoInfo.numSubSamples;
|
||||
numBytesOfClearData = frameworkCryptoInfo.numBytesOfClearData;
|
||||
numBytesOfEncryptedData = frameworkCryptoInfo.numBytesOfEncryptedData;
|
||||
key = frameworkCryptoInfo.key;
|
||||
iv = frameworkCryptoInfo.iv;
|
||||
mode = frameworkCryptoInfo.mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an equivalent {@link android.media.MediaCodec.CryptoInfo} instance.
|
||||
* <p>
|
||||
* Successive calls to this method on a single {@link CryptoInfo} will return the same instance.
|
||||
* Changes to the {@link CryptoInfo} will be reflected in the returned object. The return object
|
||||
* should not be modified directly.
|
||||
*
|
||||
* @return The equivalent {@link android.media.MediaCodec.CryptoInfo} instance.
|
||||
*/
|
||||
@TargetApi(16)
|
||||
public android.media.MediaCodec.CryptoInfo getFrameworkCryptoInfoV16() {
|
||||
return frameworkCryptoInfo;
|
||||
}
|
||||
|
||||
@TargetApi(16)
|
||||
private android.media.MediaCodec.CryptoInfo newFrameworkCryptoInfoV16() {
|
||||
return new android.media.MediaCodec.CryptoInfo();
|
||||
}
|
||||
|
||||
@TargetApi(16)
|
||||
private void updateFrameworkCryptoInfoV16() {
|
||||
frameworkCryptoInfo.set(numSubSamples, numBytesOfClearData, numBytesOfEncryptedData, key, iv,
|
||||
mode);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import org.telegram.messenger.exoplayer.util.Util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.media.MediaCodecInfo.CodecCapabilities;
|
||||
|
||||
/**
|
||||
* Contains information about a media decoder.
|
||||
*/
|
||||
@TargetApi(16)
|
||||
public final class DecoderInfo {
|
||||
|
||||
/**
|
||||
* The name of the decoder.
|
||||
* <p>
|
||||
* May be passed to {@link android.media.MediaCodec#createByCodecName(String)} to create an
|
||||
* instance of the decoder.
|
||||
*/
|
||||
public final String name;
|
||||
|
||||
/**
|
||||
* {@link CodecCapabilities} for this decoder.
|
||||
*/
|
||||
public final CodecCapabilities capabilities;
|
||||
|
||||
/**
|
||||
* Whether the decoder supports seamless resolution switches.
|
||||
*
|
||||
* @see android.media.MediaCodecInfo.CodecCapabilities#isFeatureSupported(String)
|
||||
* @see android.media.MediaCodecInfo.CodecCapabilities#FEATURE_AdaptivePlayback
|
||||
*/
|
||||
public final boolean adaptive;
|
||||
|
||||
/**
|
||||
* @param name The name of the decoder.
|
||||
* @param capabilities {@link CodecCapabilities} of the decoder.
|
||||
*/
|
||||
/* package */ DecoderInfo(String name, CodecCapabilities capabilities) {
|
||||
this.name = name;
|
||||
this.capabilities = capabilities;
|
||||
this.adaptive = isAdaptive(capabilities);
|
||||
}
|
||||
|
||||
private static boolean isAdaptive(CodecCapabilities capabilities) {
|
||||
return capabilities != null && Util.SDK_INT >= 19 && isAdaptiveV19(capabilities);
|
||||
}
|
||||
|
||||
@TargetApi(19)
|
||||
private static boolean isAdaptiveV19(CodecCapabilities capabilities) {
|
||||
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import org.telegram.messenger.exoplayer.upstream.Allocator;
|
||||
import org.telegram.messenger.exoplayer.upstream.NetworkLock;
|
||||
|
||||
import android.os.Handler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A {@link LoadControl} implementation that allows loads to continue in a sequence that prevents
|
||||
* any loader from getting too far ahead or behind any of the other loaders.
|
||||
* <p>
|
||||
* Loads are scheduled so as to fill the available buffer space as rapidly as possible. Once the
|
||||
* duration of buffered media and the buffer utilization both exceed respective thresholds, the
|
||||
* control switches to a draining state during which no loads are permitted to start. During
|
||||
* draining periods, resources such as the device radio have an opportunity to switch into low
|
||||
* power modes. The control reverts back to the loading state when either the duration of buffered
|
||||
* media or the buffer utilization fall below respective thresholds.
|
||||
* <p>
|
||||
* This implementation of {@link LoadControl} integrates with {@link NetworkLock}, by registering
|
||||
* itself as a task with priority {@link NetworkLock#STREAMING_PRIORITY} during loading periods,
|
||||
* and unregistering itself during draining periods.
|
||||
*/
|
||||
public final class DefaultLoadControl implements LoadControl {
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be notified of {@link DefaultLoadControl} events.
|
||||
*/
|
||||
public interface EventListener {
|
||||
|
||||
/**
|
||||
* Invoked when the control transitions from a loading to a draining state, or vice versa.
|
||||
*
|
||||
* @param loading Whether the control is now in a loading state.
|
||||
*/
|
||||
void onLoadingChanged(boolean loading);
|
||||
|
||||
}
|
||||
|
||||
public static final int DEFAULT_LOW_WATERMARK_MS = 15000;
|
||||
public static final int DEFAULT_HIGH_WATERMARK_MS = 30000;
|
||||
public static final float DEFAULT_LOW_BUFFER_LOAD = 0.2f;
|
||||
public static final float DEFAULT_HIGH_BUFFER_LOAD = 0.8f;
|
||||
|
||||
private static final int ABOVE_HIGH_WATERMARK = 0;
|
||||
private static final int BETWEEN_WATERMARKS = 1;
|
||||
private static final int BELOW_LOW_WATERMARK = 2;
|
||||
|
||||
private final Allocator allocator;
|
||||
private final List<Object> loaders;
|
||||
private final HashMap<Object, LoaderState> loaderStates;
|
||||
private final Handler eventHandler;
|
||||
private final EventListener eventListener;
|
||||
|
||||
private final long lowWatermarkUs;
|
||||
private final long highWatermarkUs;
|
||||
private final float lowBufferLoad;
|
||||
private final float highBufferLoad;
|
||||
|
||||
private int targetBufferSize;
|
||||
private long maxLoadStartPositionUs;
|
||||
private int bufferState;
|
||||
private boolean fillingBuffers;
|
||||
private boolean streamingPrioritySet;
|
||||
|
||||
/**
|
||||
* Constructs a new instance, using the {@code DEFAULT_*} constants defined in this class.
|
||||
*
|
||||
* @param allocator The {@link Allocator} used by the loader.
|
||||
*/
|
||||
public DefaultLoadControl(Allocator allocator) {
|
||||
this(allocator, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance, using the {@code DEFAULT_*} constants defined in this class.
|
||||
*
|
||||
* @param allocator The {@link Allocator} used by the loader.
|
||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||
* null if delivery of events is not required.
|
||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||
*/
|
||||
public DefaultLoadControl(Allocator allocator, Handler eventHandler,
|
||||
EventListener eventListener) {
|
||||
this(allocator, eventHandler, eventListener, DEFAULT_LOW_WATERMARK_MS,
|
||||
DEFAULT_HIGH_WATERMARK_MS, DEFAULT_LOW_BUFFER_LOAD, DEFAULT_HIGH_BUFFER_LOAD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
*
|
||||
* @param allocator The {@link Allocator} used by the loader.
|
||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||
* null if delivery of events is not required.
|
||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||
* @param lowWatermarkMs The minimum duration of media that can be buffered for the control to
|
||||
* be in the draining state. If less media is buffered, then the control will transition to
|
||||
* the filling state.
|
||||
* @param highWatermarkMs The minimum duration of media that can be buffered for the control to
|
||||
* transition from filling to draining.
|
||||
* @param lowBufferLoad The minimum fraction of the buffer that must be utilized for the control
|
||||
* to be in the draining state. If the utilization is lower, then the control will transition
|
||||
* to the filling state.
|
||||
* @param highBufferLoad The minimum fraction of the buffer that must be utilized for the control
|
||||
* to transition from the loading state to the draining state.
|
||||
*/
|
||||
public DefaultLoadControl(Allocator allocator, Handler eventHandler, EventListener eventListener,
|
||||
int lowWatermarkMs, int highWatermarkMs, float lowBufferLoad, float highBufferLoad) {
|
||||
this.allocator = allocator;
|
||||
this.eventHandler = eventHandler;
|
||||
this.eventListener = eventListener;
|
||||
this.loaders = new ArrayList<>();
|
||||
this.loaderStates = new HashMap<>();
|
||||
this.lowWatermarkUs = lowWatermarkMs * 1000L;
|
||||
this.highWatermarkUs = highWatermarkMs * 1000L;
|
||||
this.lowBufferLoad = lowBufferLoad;
|
||||
this.highBufferLoad = highBufferLoad;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(Object loader, int bufferSizeContribution) {
|
||||
loaders.add(loader);
|
||||
loaderStates.put(loader, new LoaderState(bufferSizeContribution));
|
||||
targetBufferSize += bufferSizeContribution;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister(Object loader) {
|
||||
loaders.remove(loader);
|
||||
LoaderState state = loaderStates.remove(loader);
|
||||
targetBufferSize -= state.bufferSizeContribution;
|
||||
updateControlState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trimAllocator() {
|
||||
allocator.trim(targetBufferSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Allocator getAllocator() {
|
||||
return allocator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean update(Object loader, long playbackPositionUs, long nextLoadPositionUs,
|
||||
boolean loading) {
|
||||
// Update the loader state.
|
||||
int loaderBufferState = getLoaderBufferState(playbackPositionUs, nextLoadPositionUs);
|
||||
LoaderState loaderState = loaderStates.get(loader);
|
||||
boolean loaderStateChanged = loaderState.bufferState != loaderBufferState
|
||||
|| loaderState.nextLoadPositionUs != nextLoadPositionUs || loaderState.loading != loading;
|
||||
if (loaderStateChanged) {
|
||||
loaderState.bufferState = loaderBufferState;
|
||||
loaderState.nextLoadPositionUs = nextLoadPositionUs;
|
||||
loaderState.loading = loading;
|
||||
}
|
||||
|
||||
// Update the buffer state.
|
||||
int currentBufferSize = allocator.getTotalBytesAllocated();
|
||||
int bufferState = getBufferState(currentBufferSize);
|
||||
boolean bufferStateChanged = this.bufferState != bufferState;
|
||||
if (bufferStateChanged) {
|
||||
this.bufferState = bufferState;
|
||||
}
|
||||
|
||||
// If either of the individual states have changed, update the shared control state.
|
||||
if (loaderStateChanged || bufferStateChanged) {
|
||||
updateControlState();
|
||||
}
|
||||
|
||||
return currentBufferSize < targetBufferSize && nextLoadPositionUs != -1
|
||||
&& nextLoadPositionUs <= maxLoadStartPositionUs;
|
||||
}
|
||||
|
||||
private int getLoaderBufferState(long playbackPositionUs, long nextLoadPositionUs) {
|
||||
if (nextLoadPositionUs == -1) {
|
||||
return ABOVE_HIGH_WATERMARK;
|
||||
} else {
|
||||
long timeUntilNextLoadPosition = nextLoadPositionUs - playbackPositionUs;
|
||||
return timeUntilNextLoadPosition > highWatermarkUs ? ABOVE_HIGH_WATERMARK :
|
||||
timeUntilNextLoadPosition < lowWatermarkUs ? BELOW_LOW_WATERMARK :
|
||||
BETWEEN_WATERMARKS;
|
||||
}
|
||||
}
|
||||
|
||||
private int getBufferState(int currentBufferSize) {
|
||||
float bufferLoad = (float) currentBufferSize / targetBufferSize;
|
||||
return bufferLoad > highBufferLoad ? ABOVE_HIGH_WATERMARK
|
||||
: bufferLoad < lowBufferLoad ? BELOW_LOW_WATERMARK
|
||||
: BETWEEN_WATERMARKS;
|
||||
}
|
||||
|
||||
private void updateControlState() {
|
||||
boolean loading = false;
|
||||
boolean haveNextLoadPosition = false;
|
||||
int highestState = bufferState;
|
||||
for (int i = 0; i < loaders.size(); i++) {
|
||||
LoaderState loaderState = loaderStates.get(loaders.get(i));
|
||||
loading |= loaderState.loading;
|
||||
haveNextLoadPosition |= loaderState.nextLoadPositionUs != -1;
|
||||
highestState = Math.max(highestState, loaderState.bufferState);
|
||||
}
|
||||
|
||||
fillingBuffers = !loaders.isEmpty() && (loading || haveNextLoadPosition)
|
||||
&& (highestState == BELOW_LOW_WATERMARK
|
||||
|| (highestState == BETWEEN_WATERMARKS && fillingBuffers));
|
||||
if (fillingBuffers && !streamingPrioritySet) {
|
||||
NetworkLock.instance.add(NetworkLock.STREAMING_PRIORITY);
|
||||
streamingPrioritySet = true;
|
||||
notifyLoadingChanged(true);
|
||||
} else if (!fillingBuffers && streamingPrioritySet && !loading) {
|
||||
NetworkLock.instance.remove(NetworkLock.STREAMING_PRIORITY);
|
||||
streamingPrioritySet = false;
|
||||
notifyLoadingChanged(false);
|
||||
}
|
||||
|
||||
maxLoadStartPositionUs = -1;
|
||||
if (fillingBuffers) {
|
||||
for (int i = 0; i < loaders.size(); i++) {
|
||||
Object loader = loaders.get(i);
|
||||
LoaderState loaderState = loaderStates.get(loader);
|
||||
long loaderTime = loaderState.nextLoadPositionUs;
|
||||
if (loaderTime != -1
|
||||
&& (maxLoadStartPositionUs == -1 || loaderTime < maxLoadStartPositionUs)) {
|
||||
maxLoadStartPositionUs = loaderTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyLoadingChanged(final boolean loading) {
|
||||
if (eventHandler != null && eventListener != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onLoadingChanged(loading);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static class LoaderState {
|
||||
|
||||
public final int bufferSizeContribution;
|
||||
|
||||
public int bufferState;
|
||||
public boolean loading;
|
||||
public long nextLoadPositionUs;
|
||||
|
||||
public LoaderState(int bufferSizeContribution) {
|
||||
this.bufferSizeContribution = bufferSizeContribution;
|
||||
bufferState = ABOVE_HIGH_WATERMARK;
|
||||
loading = false;
|
||||
nextLoadPositionUs = -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
/**
|
||||
* A {@link TrackRenderer} that does nothing.
|
||||
* <p>
|
||||
* This renderer returns 0 from {@link #getTrackCount()} in order to request that it should be
|
||||
* ignored. {@link IllegalStateException} is thrown from all other methods documented to indicate
|
||||
* that they should not be invoked unless the renderer is prepared.
|
||||
*/
|
||||
public final class DummyTrackRenderer extends TrackRenderer {
|
||||
|
||||
@Override
|
||||
protected boolean doPrepare(long positionUs) throws ExoPlaybackException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getTrackCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MediaFormat getFormat(int track) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnded() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isReady() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void seekTo(long positionUs) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void maybeThrowError() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long getDurationUs() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long getBufferedPositionUs() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
/**
|
||||
* Thrown when a non-recoverable playback failure occurs.
|
||||
* <p>
|
||||
* Where possible, the cause returned by {@link #getCause()} will indicate the reason for failure.
|
||||
*/
|
||||
public final class ExoPlaybackException extends Exception {
|
||||
|
||||
/**
|
||||
* True if the cause (i.e. the {@link Throwable} returned by {@link #getCause()}) was only caught
|
||||
* by a fail-safe at the top level of the player. False otherwise.
|
||||
*/
|
||||
public final boolean caughtAtTopLevel;
|
||||
|
||||
public ExoPlaybackException(String message) {
|
||||
super(message);
|
||||
caughtAtTopLevel = false;
|
||||
}
|
||||
|
||||
public ExoPlaybackException(Throwable cause) {
|
||||
super(cause);
|
||||
caughtAtTopLevel = false;
|
||||
}
|
||||
|
||||
public ExoPlaybackException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
caughtAtTopLevel = false;
|
||||
}
|
||||
|
||||
/* package */ ExoPlaybackException(Throwable cause, boolean caughtAtTopLevel) {
|
||||
super(cause);
|
||||
this.caughtAtTopLevel = caughtAtTopLevel;
|
||||
}
|
||||
|
||||
}
|
415
TMessagesProj/src/main/java/org/telegram/messenger/exoplayer/ExoPlayer.java
Executable file
415
TMessagesProj/src/main/java/org/telegram/messenger/exoplayer/ExoPlayer.java
Executable file
|
@ -0,0 +1,415 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import android.os.Looper;
|
||||
|
||||
/**
|
||||
* An extensible media player exposing traditional high-level media player functionality, such as
|
||||
* the ability to prepare, play, pause and seek.
|
||||
*
|
||||
* <p>Topics covered here are:
|
||||
* <ol>
|
||||
* <li><a href="#Assumptions">Assumptions and player composition</a>
|
||||
* <li><a href="#Threading">Threading model</a>
|
||||
* <li><a href="#State">Player state</a>
|
||||
* </ol>
|
||||
*
|
||||
* <a name="Assumptions"></a>
|
||||
* <h3>Assumptions and player construction</h3>
|
||||
*
|
||||
* <p>The implementation is designed to make no assumptions about (and hence impose no restrictions
|
||||
* on) the type of the media being played, how and where it is stored, or how it is rendered.
|
||||
* Rather than implementing the loading and rendering of media directly, {@link ExoPlayer} instead
|
||||
* delegates this work to one or more {@link TrackRenderer}s, which are injected when the player
|
||||
* is prepared. Hence {@link ExoPlayer} is capable of loading and playing any media for which a
|
||||
* {@link TrackRenderer} implementation can be provided.
|
||||
*
|
||||
* <p>{@link MediaCodecAudioTrackRenderer} and {@link MediaCodecVideoTrackRenderer} can be used for
|
||||
* the common cases of rendering audio and video. These components in turn require an
|
||||
* <i>upstream</i> {@link SampleSource} to be injected through their constructors, where upstream
|
||||
* is defined to denote a component that is closer to the source of the media. This pattern of
|
||||
* upstream dependency injection is actively encouraged, since it means that the functionality of
|
||||
* the player is built up through the composition of components that can easily be exchanged for
|
||||
* alternate implementations. For example a {@link SampleSource} implementation may require a
|
||||
* further upstream data loading component to be injected through its constructor, with different
|
||||
* implementations enabling the loading of data from various sources.
|
||||
*
|
||||
* <a name="Threading"></a>
|
||||
* <h3>Threading model</h3>
|
||||
*
|
||||
* <p>The figure below shows the {@link ExoPlayer} threading model.</p>
|
||||
* <p align="center"><img src="../../../../../images/exoplayer_threading_model.png"
|
||||
* alt="MediaPlayer state diagram"
|
||||
* border="0"/></p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>It is recommended that instances are created and accessed from a single application thread.
|
||||
* An application's main thread is ideal. Accessing an instance from multiple threads is
|
||||
* discouraged, however if an application does wish to do this then it may do so provided that it
|
||||
* ensures accesses are synchronized.
|
||||
* </li>
|
||||
* <li>Registered {@link Listener}s are invoked on the thread that created the {@link ExoPlayer}
|
||||
* instance.</li>
|
||||
* <li>An internal playback thread is responsible for managing playback and invoking the
|
||||
* {@link TrackRenderer}s in order to load and play the media.</li>
|
||||
* <li>{@link TrackRenderer} implementations (or any upstream components that they depend on) may
|
||||
* use additional background threads (e.g. to load data). These are implementation specific.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <a name="State"></a>
|
||||
* <h3>Player state</h3>
|
||||
*
|
||||
* <p>The components of an {@link ExoPlayer}'s state can be divided into two distinct groups. State
|
||||
* accessed by {@link #getSelectedTrack(int)} and {@link #getPlayWhenReady()} is only ever
|
||||
* changed by invoking the player's methods, and are never changed as a result of operations that
|
||||
* have been performed asynchronously by the playback thread. In contrast, the playback state
|
||||
* accessed by {@link #getPlaybackState()} is only ever changed as a result of operations
|
||||
* completing on the playback thread, as illustrated below.</p>
|
||||
* <p align="center"><img src="../../../../../images/exoplayer_state.png"
|
||||
* alt="ExoPlayer state"
|
||||
* border="0"/></p>
|
||||
*
|
||||
* <p>The possible playback state transitions are shown below. Transitions can be triggered either
|
||||
* by changes in the state of the {@link TrackRenderer}s being used, or as a result of
|
||||
* {@link #prepare(TrackRenderer[])}, {@link #stop()} or {@link #release()} being invoked.</p>
|
||||
* <p align="center"><img src="../../../../../images/exoplayer_playbackstate.png"
|
||||
* alt="ExoPlayer playback state transitions"
|
||||
* border="0"/></p>
|
||||
*/
|
||||
public interface ExoPlayer {
|
||||
|
||||
/**
|
||||
* A factory for instantiating ExoPlayer instances.
|
||||
*/
|
||||
public static final class Factory {
|
||||
|
||||
/**
|
||||
* The default minimum duration of data that must be buffered for playback to start or resume
|
||||
* following a user action such as a seek.
|
||||
*/
|
||||
public static final int DEFAULT_MIN_BUFFER_MS = 2500;
|
||||
|
||||
/**
|
||||
* The default minimum duration of data that must be buffered for playback to resume
|
||||
* after a player invoked rebuffer (i.e. a rebuffer that occurs due to buffer depletion, and
|
||||
* not due to a user action such as starting playback or seeking).
|
||||
*/
|
||||
public static final int DEFAULT_MIN_REBUFFER_MS = 5000;
|
||||
|
||||
private Factory() {}
|
||||
|
||||
/**
|
||||
* Obtains an {@link ExoPlayer} instance.
|
||||
* <p>
|
||||
* Must be invoked from a thread that has an associated {@link Looper}.
|
||||
*
|
||||
* @param rendererCount The number of {@link TrackRenderer}s that will be passed to
|
||||
* {@link #prepare(TrackRenderer[])}.
|
||||
* @param minBufferMs A minimum duration of data that must be buffered for playback to start
|
||||
* or resume following a user action such as a seek.
|
||||
* @param minRebufferMs A minimum duration of data that must be buffered for playback to resume
|
||||
* after a player invoked rebuffer (i.e. a rebuffer that occurs due to buffer depletion, and
|
||||
* not due to a user action such as starting playback or seeking).
|
||||
*/
|
||||
public static ExoPlayer newInstance(int rendererCount, int minBufferMs, int minRebufferMs) {
|
||||
return new ExoPlayerImpl(rendererCount, minBufferMs, minRebufferMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains an {@link ExoPlayer} instance.
|
||||
* <p>
|
||||
* Must be invoked from a thread that has an associated {@link Looper}.
|
||||
*
|
||||
* @param rendererCount The number of {@link TrackRenderer}s that will be passed to
|
||||
* {@link #prepare(TrackRenderer[])}.
|
||||
*/
|
||||
public static ExoPlayer newInstance(int rendererCount) {
|
||||
return new ExoPlayerImpl(rendererCount, DEFAULT_MIN_BUFFER_MS, DEFAULT_MIN_REBUFFER_MS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be notified of changes in player state.
|
||||
*/
|
||||
public interface Listener {
|
||||
/**
|
||||
* Invoked when the value returned from either {@link ExoPlayer#getPlayWhenReady()} or
|
||||
* {@link ExoPlayer#getPlaybackState()} changes.
|
||||
*
|
||||
* @param playWhenReady Whether playback will proceed when ready.
|
||||
* @param playbackState One of the {@code STATE} constants defined in the {@link ExoPlayer}
|
||||
* interface.
|
||||
*/
|
||||
void onPlayerStateChanged(boolean playWhenReady, int playbackState);
|
||||
/**
|
||||
* Invoked when the current value of {@link ExoPlayer#getPlayWhenReady()} has been reflected
|
||||
* by the internal playback thread.
|
||||
* <p>
|
||||
* An invocation of this method will shortly follow any call to
|
||||
* {@link ExoPlayer#setPlayWhenReady(boolean)} that changes the state. If multiple calls are
|
||||
* made in rapid succession, then this method will be invoked only once, after the final state
|
||||
* has been reflected.
|
||||
*/
|
||||
void onPlayWhenReadyCommitted();
|
||||
/**
|
||||
* Invoked when an error occurs. The playback state will transition to
|
||||
* {@link ExoPlayer#STATE_IDLE} immediately after this method is invoked. The player instance
|
||||
* can still be used, and {@link ExoPlayer#release()} must still be called on the player should
|
||||
* it no longer be required.
|
||||
*
|
||||
* @param error The error.
|
||||
*/
|
||||
void onPlayerError(ExoPlaybackException error);
|
||||
}
|
||||
|
||||
/**
|
||||
* A component of an {@link ExoPlayer} that can receive messages on the playback thread.
|
||||
* <p>
|
||||
* Messages can be delivered to a component via {@link ExoPlayer#sendMessage} and
|
||||
* {@link ExoPlayer#blockingSendMessage}.
|
||||
*/
|
||||
public interface ExoPlayerComponent {
|
||||
|
||||
/**
|
||||
* Handles a message delivered to the component. Invoked on the playback thread.
|
||||
*
|
||||
* @param messageType An integer identifying the type of message.
|
||||
* @param message The message object.
|
||||
* @throws ExoPlaybackException If an error occurred whilst handling the message.
|
||||
*/
|
||||
void handleMessage(int messageType, Object message) throws ExoPlaybackException;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The player is neither prepared or being prepared.
|
||||
*/
|
||||
public static final int STATE_IDLE = 1;
|
||||
/**
|
||||
* The player is being prepared.
|
||||
*/
|
||||
public static final int STATE_PREPARING = 2;
|
||||
/**
|
||||
* The player is prepared but not able to immediately play from the current position. The cause
|
||||
* is {@link TrackRenderer} specific, but this state typically occurs when more data needs
|
||||
* to be buffered for playback to start.
|
||||
*/
|
||||
public static final int STATE_BUFFERING = 3;
|
||||
/**
|
||||
* The player is prepared and able to immediately play from the current position. The player will
|
||||
* be playing if {@link #getPlayWhenReady()} returns true, and paused otherwise.
|
||||
*/
|
||||
public static final int STATE_READY = 4;
|
||||
/**
|
||||
* The player has finished playing the media.
|
||||
*/
|
||||
public static final int STATE_ENDED = 5;
|
||||
|
||||
/**
|
||||
* A value that can be passed as the second argument to {@link #setSelectedTrack(int, int)} to
|
||||
* disable the renderer.
|
||||
*/
|
||||
public static final int TRACK_DISABLED = -1;
|
||||
/**
|
||||
* A value that can be passed as the second argument to {@link #setSelectedTrack(int, int)} to
|
||||
* select the default track.
|
||||
*/
|
||||
public static final int TRACK_DEFAULT = 0;
|
||||
|
||||
/**
|
||||
* Represents an unknown time or duration.
|
||||
*/
|
||||
public static final long UNKNOWN_TIME = -1;
|
||||
|
||||
/**
|
||||
* Gets the {@link Looper} associated with the playback thread.
|
||||
*
|
||||
* @return The {@link Looper} associated with the playback thread.
|
||||
*/
|
||||
public Looper getPlaybackLooper();
|
||||
|
||||
/**
|
||||
* Register a listener to receive events from the player. The listener's methods will be invoked
|
||||
* on the thread that was used to construct the player.
|
||||
*
|
||||
* @param listener The listener to register.
|
||||
*/
|
||||
public void addListener(Listener listener);
|
||||
|
||||
/**
|
||||
* Unregister a listener. The listener will no longer receive events from the player.
|
||||
*
|
||||
* @param listener The listener to unregister.
|
||||
*/
|
||||
public void removeListener(Listener listener);
|
||||
|
||||
/**
|
||||
* Returns the current state of the player.
|
||||
*
|
||||
* @return One of the {@code STATE} constants defined in this interface.
|
||||
*/
|
||||
public int getPlaybackState();
|
||||
|
||||
/**
|
||||
* Prepares the player for playback.
|
||||
*
|
||||
* @param renderers The {@link TrackRenderer}s to use. The number of renderers must match the
|
||||
* value that was passed to the {@link ExoPlayer.Factory#newInstance} method.
|
||||
*/
|
||||
public void prepare(TrackRenderer... renderers);
|
||||
|
||||
/**
|
||||
* Returns the number of tracks exposed by the specified renderer.
|
||||
*
|
||||
* @param rendererIndex The index of the renderer.
|
||||
* @return The number of tracks.
|
||||
*/
|
||||
public int getTrackCount(int rendererIndex);
|
||||
|
||||
/**
|
||||
* Returns the format of a track.
|
||||
*
|
||||
* @param rendererIndex The index of the renderer.
|
||||
* @param trackIndex The index of the track.
|
||||
* @return The format of the track.
|
||||
*/
|
||||
public MediaFormat getTrackFormat(int rendererIndex, int trackIndex);
|
||||
|
||||
/**
|
||||
* Selects a track for the specified renderer.
|
||||
*
|
||||
* @param rendererIndex The index of the renderer.
|
||||
* @param trackIndex The index of the track. A negative value or a value greater than or equal to
|
||||
* the renderer's track count will disable the renderer.
|
||||
*/
|
||||
public void setSelectedTrack(int rendererIndex, int trackIndex);
|
||||
|
||||
/**
|
||||
* Returns the index of the currently selected track for the specified renderer.
|
||||
*
|
||||
* @param rendererIndex The index of the renderer.
|
||||
* @return The selected track. A negative value or a value greater than or equal to the renderer's
|
||||
* track count indicates that the renderer is disabled.
|
||||
*/
|
||||
public int getSelectedTrack(int rendererIndex);
|
||||
|
||||
/**
|
||||
* Sets whether playback should proceed when {@link #getPlaybackState()} == {@link #STATE_READY}.
|
||||
* If the player is already in this state, then this method can be used to pause and resume
|
||||
* playback.
|
||||
*
|
||||
* @param playWhenReady Whether playback should proceed when ready.
|
||||
*/
|
||||
public void setPlayWhenReady(boolean playWhenReady);
|
||||
|
||||
/**
|
||||
* Whether playback will proceed when {@link #getPlaybackState()} == {@link #STATE_READY}.
|
||||
*
|
||||
* @return Whether playback will proceed when ready.
|
||||
*/
|
||||
public boolean getPlayWhenReady();
|
||||
|
||||
/**
|
||||
* Whether the current value of {@link ExoPlayer#getPlayWhenReady()} has been reflected by the
|
||||
* internal playback thread.
|
||||
*
|
||||
* @return True if the current value has been reflected. False otherwise.
|
||||
*/
|
||||
public boolean isPlayWhenReadyCommitted();
|
||||
|
||||
/**
|
||||
* Seeks to a position specified in milliseconds.
|
||||
*
|
||||
* @param positionMs The seek position.
|
||||
*/
|
||||
public void seekTo(long positionMs);
|
||||
|
||||
/**
|
||||
* Stops playback. Use {@code setPlayWhenReady(false)} rather than this method if the intention
|
||||
* is to pause playback.
|
||||
* <p>
|
||||
* Calling this method will cause the playback state to transition to
|
||||
* {@link ExoPlayer#STATE_IDLE}. The player instance can still be used, and
|
||||
* {@link ExoPlayer#release()} must still be called on the player if it's no longer required.
|
||||
* <p>
|
||||
* Calling this method does not reset the playback position. If this player instance will be used
|
||||
* to play another video from its start, then {@code seekTo(0)} should be called after stopping
|
||||
* the player and before preparing it for the next video.
|
||||
*/
|
||||
public void stop();
|
||||
|
||||
/**
|
||||
* Releases the player. This method must be called when the player is no longer required.
|
||||
* <p>
|
||||
* The player must not be used after calling this method.
|
||||
*/
|
||||
public void release();
|
||||
|
||||
/**
|
||||
* Sends a message to a specified component. The message is delivered to the component on the
|
||||
* playback thread. If the component throws a {@link ExoPlaybackException}, then it is
|
||||
* propagated out of the player as an error.
|
||||
*
|
||||
* @param target The target to which the message should be delivered.
|
||||
* @param messageType An integer that can be used to identify the type of the message.
|
||||
* @param message The message object.
|
||||
*/
|
||||
public void sendMessage(ExoPlayerComponent target, int messageType, Object message);
|
||||
|
||||
/**
|
||||
* Blocking variant of {@link #sendMessage(ExoPlayerComponent, int, Object)} that does not return
|
||||
* until after the message has been delivered.
|
||||
*
|
||||
* @param target The target to which the message should be delivered.
|
||||
* @param messageType An integer that can be used to identify the type of the message.
|
||||
* @param message The message object.
|
||||
*/
|
||||
public void blockingSendMessage(ExoPlayerComponent target, int messageType, Object message);
|
||||
|
||||
/**
|
||||
* Gets the duration of the track in milliseconds.
|
||||
*
|
||||
* @return The duration of the track in milliseconds, or {@link ExoPlayer#UNKNOWN_TIME} if the
|
||||
* duration is not known.
|
||||
*/
|
||||
public long getDuration();
|
||||
|
||||
/**
|
||||
* Gets the current playback position in milliseconds.
|
||||
*
|
||||
* @return The current playback position in milliseconds.
|
||||
*/
|
||||
public long getCurrentPosition();
|
||||
|
||||
/**
|
||||
* Gets an estimate of the absolute position in milliseconds up to which data is buffered.
|
||||
*
|
||||
* @return An estimate of the absolute position in milliseconds up to which data is buffered,
|
||||
* or {@link ExoPlayer#UNKNOWN_TIME} if no estimate is available.
|
||||
*/
|
||||
public long getBufferedPosition();
|
||||
|
||||
/**
|
||||
* Gets an estimate of the percentage into the media up to which data is buffered.
|
||||
*
|
||||
* @return An estimate of the percentage into the media up to which data is buffered. 0 if the
|
||||
* duration of the media is not known or if no estimate is available.
|
||||
*/
|
||||
public int getBufferedPercentage();
|
||||
|
||||
}
|
230
TMessagesProj/src/main/java/org/telegram/messenger/exoplayer/ExoPlayerImpl.java
Executable file
230
TMessagesProj/src/main/java/org/telegram/messenger/exoplayer/ExoPlayerImpl.java
Executable file
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
/**
|
||||
* Concrete implementation of {@link ExoPlayer}.
|
||||
*/
|
||||
/* package */ final class ExoPlayerImpl implements ExoPlayer {
|
||||
|
||||
private static final String TAG = "ExoPlayerImpl";
|
||||
|
||||
private final Handler eventHandler;
|
||||
private final ExoPlayerImplInternal internalPlayer;
|
||||
private final CopyOnWriteArraySet<Listener> listeners;
|
||||
private final MediaFormat[][] trackFormats;
|
||||
private final int[] selectedTrackIndices;
|
||||
|
||||
private boolean playWhenReady;
|
||||
private int playbackState;
|
||||
private int pendingPlayWhenReadyAcks;
|
||||
|
||||
/**
|
||||
* Constructs an instance. Must be invoked from a thread that has an associated {@link Looper}.
|
||||
*
|
||||
* @param rendererCount The number of {@link TrackRenderer}s that will be passed to
|
||||
* {@link #prepare(TrackRenderer[])}.
|
||||
* @param minBufferMs A minimum duration of data that must be buffered for playback to start
|
||||
* or resume following a user action such as a seek.
|
||||
* @param minRebufferMs A minimum duration of data that must be buffered for playback to resume
|
||||
* after a player invoked rebuffer (i.e. a rebuffer that occurs due to buffer depletion, and
|
||||
* not due to a user action such as starting playback or seeking).
|
||||
*/
|
||||
@SuppressLint("HandlerLeak")
|
||||
public ExoPlayerImpl(int rendererCount, int minBufferMs, int minRebufferMs) {
|
||||
Log.i(TAG, "Init " + ExoPlayerLibraryInfo.VERSION);
|
||||
this.playWhenReady = false;
|
||||
this.playbackState = STATE_IDLE;
|
||||
this.listeners = new CopyOnWriteArraySet<>();
|
||||
this.trackFormats = new MediaFormat[rendererCount][];
|
||||
this.selectedTrackIndices = new int[rendererCount];
|
||||
eventHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
ExoPlayerImpl.this.handleEvent(msg);
|
||||
}
|
||||
};
|
||||
internalPlayer = new ExoPlayerImplInternal(eventHandler, playWhenReady, selectedTrackIndices,
|
||||
minBufferMs, minRebufferMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Looper getPlaybackLooper() {
|
||||
return internalPlayer.getPlaybackLooper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(Listener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(Listener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlaybackState() {
|
||||
return playbackState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare(TrackRenderer... renderers) {
|
||||
Arrays.fill(trackFormats, null);
|
||||
internalPlayer.prepare(renderers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTrackCount(int rendererIndex) {
|
||||
return trackFormats[rendererIndex] != null ? trackFormats[rendererIndex].length : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaFormat getTrackFormat(int rendererIndex, int trackIndex) {
|
||||
return trackFormats[rendererIndex][trackIndex];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectedTrack(int rendererIndex, int trackIndex) {
|
||||
if (selectedTrackIndices[rendererIndex] != trackIndex) {
|
||||
selectedTrackIndices[rendererIndex] = trackIndex;
|
||||
internalPlayer.setRendererSelectedTrack(rendererIndex, trackIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSelectedTrack(int rendererIndex) {
|
||||
return selectedTrackIndices[rendererIndex];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayWhenReady(boolean playWhenReady) {
|
||||
if (this.playWhenReady != playWhenReady) {
|
||||
this.playWhenReady = playWhenReady;
|
||||
pendingPlayWhenReadyAcks++;
|
||||
internalPlayer.setPlayWhenReady(playWhenReady);
|
||||
for (Listener listener : listeners) {
|
||||
listener.onPlayerStateChanged(playWhenReady, playbackState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPlayWhenReady() {
|
||||
return playWhenReady;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayWhenReadyCommitted() {
|
||||
return pendingPlayWhenReadyAcks == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekTo(long positionMs) {
|
||||
internalPlayer.seekTo(positionMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
internalPlayer.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
internalPlayer.release();
|
||||
eventHandler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(ExoPlayerComponent target, int messageType, Object message) {
|
||||
internalPlayer.sendMessage(target, messageType, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blockingSendMessage(ExoPlayerComponent target, int messageType, Object message) {
|
||||
internalPlayer.blockingSendMessage(target, messageType, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
return internalPlayer.getDuration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCurrentPosition() {
|
||||
return internalPlayer.getCurrentPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getBufferedPosition() {
|
||||
return internalPlayer.getBufferedPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBufferedPercentage() {
|
||||
long bufferedPosition = getBufferedPosition();
|
||||
long duration = getDuration();
|
||||
return bufferedPosition == ExoPlayer.UNKNOWN_TIME || duration == ExoPlayer.UNKNOWN_TIME ? 0
|
||||
: (int) (duration == 0 ? 100 : (bufferedPosition * 100) / duration);
|
||||
}
|
||||
|
||||
// Not private so it can be called from an inner class without going through a thunk method.
|
||||
/* package */ void handleEvent(Message msg) {
|
||||
switch (msg.what) {
|
||||
case ExoPlayerImplInternal.MSG_PREPARED: {
|
||||
System.arraycopy(msg.obj, 0, trackFormats, 0, trackFormats.length);
|
||||
playbackState = msg.arg1;
|
||||
for (Listener listener : listeners) {
|
||||
listener.onPlayerStateChanged(playWhenReady, playbackState);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExoPlayerImplInternal.MSG_STATE_CHANGED: {
|
||||
playbackState = msg.arg1;
|
||||
for (Listener listener : listeners) {
|
||||
listener.onPlayerStateChanged(playWhenReady, playbackState);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExoPlayerImplInternal.MSG_SET_PLAY_WHEN_READY_ACK: {
|
||||
pendingPlayWhenReadyAcks--;
|
||||
if (pendingPlayWhenReadyAcks == 0) {
|
||||
for (Listener listener : listeners) {
|
||||
listener.onPlayWhenReadyCommitted();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExoPlayerImplInternal.MSG_ERROR: {
|
||||
ExoPlaybackException exception = (ExoPlaybackException) msg.obj;
|
||||
for (Listener listener : listeners) {
|
||||
listener.onPlayerError(exception);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,668 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import org.telegram.messenger.exoplayer.ExoPlayer.ExoPlayerComponent;
|
||||
import org.telegram.messenger.exoplayer.util.Assertions;
|
||||
import org.telegram.messenger.exoplayer.util.PriorityHandlerThread;
|
||||
import org.telegram.messenger.exoplayer.util.TraceUtil;
|
||||
import org.telegram.messenger.exoplayer.util.Util;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Process;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Implements the internal behavior of {@link ExoPlayerImpl}.
|
||||
*/
|
||||
/* package */ final class ExoPlayerImplInternal implements Handler.Callback {
|
||||
|
||||
private static final String TAG = "ExoPlayerImplInternal";
|
||||
|
||||
// External messages
|
||||
public static final int MSG_PREPARED = 1;
|
||||
public static final int MSG_STATE_CHANGED = 2;
|
||||
public static final int MSG_SET_PLAY_WHEN_READY_ACK = 3;
|
||||
public static final int MSG_ERROR = 4;
|
||||
|
||||
// Internal messages
|
||||
private static final int MSG_PREPARE = 1;
|
||||
private static final int MSG_INCREMENTAL_PREPARE = 2;
|
||||
private static final int MSG_SET_PLAY_WHEN_READY = 3;
|
||||
private static final int MSG_STOP = 4;
|
||||
private static final int MSG_RELEASE = 5;
|
||||
private static final int MSG_SEEK_TO = 6;
|
||||
private static final int MSG_DO_SOME_WORK = 7;
|
||||
private static final int MSG_SET_RENDERER_SELECTED_TRACK = 8;
|
||||
private static final int MSG_CUSTOM = 9;
|
||||
|
||||
private static final int PREPARE_INTERVAL_MS = 10;
|
||||
private static final int RENDERING_INTERVAL_MS = 10;
|
||||
private static final int IDLE_INTERVAL_MS = 1000;
|
||||
|
||||
private final Handler handler;
|
||||
private final HandlerThread internalPlaybackThread;
|
||||
private final Handler eventHandler;
|
||||
private final StandaloneMediaClock standaloneMediaClock;
|
||||
private final AtomicInteger pendingSeekCount;
|
||||
private final List<TrackRenderer> enabledRenderers;
|
||||
private final MediaFormat[][] trackFormats;
|
||||
private final int[] selectedTrackIndices;
|
||||
private final long minBufferUs;
|
||||
private final long minRebufferUs;
|
||||
|
||||
private TrackRenderer[] renderers;
|
||||
private TrackRenderer rendererMediaClockSource;
|
||||
private MediaClock rendererMediaClock;
|
||||
|
||||
private boolean released;
|
||||
private boolean playWhenReady;
|
||||
private boolean rebuffering;
|
||||
private int state;
|
||||
private int customMessagesSent = 0;
|
||||
private int customMessagesProcessed = 0;
|
||||
private long lastSeekPositionMs;
|
||||
private long elapsedRealtimeUs;
|
||||
|
||||
private volatile long durationUs;
|
||||
private volatile long positionUs;
|
||||
private volatile long bufferedPositionUs;
|
||||
|
||||
public ExoPlayerImplInternal(Handler eventHandler, boolean playWhenReady,
|
||||
int[] selectedTrackIndices, int minBufferMs, int minRebufferMs) {
|
||||
this.eventHandler = eventHandler;
|
||||
this.playWhenReady = playWhenReady;
|
||||
this.minBufferUs = minBufferMs * 1000L;
|
||||
this.minRebufferUs = minRebufferMs * 1000L;
|
||||
this.selectedTrackIndices = Arrays.copyOf(selectedTrackIndices, selectedTrackIndices.length);
|
||||
this.state = ExoPlayer.STATE_IDLE;
|
||||
this.durationUs = TrackRenderer.UNKNOWN_TIME_US;
|
||||
this.bufferedPositionUs = TrackRenderer.UNKNOWN_TIME_US;
|
||||
|
||||
standaloneMediaClock = new StandaloneMediaClock();
|
||||
pendingSeekCount = new AtomicInteger();
|
||||
enabledRenderers = new ArrayList<>(selectedTrackIndices.length);
|
||||
trackFormats = new MediaFormat[selectedTrackIndices.length][];
|
||||
// Note: The documentation for Process.THREAD_PRIORITY_AUDIO that states "Applications can
|
||||
// not normally change to this priority" is incorrect.
|
||||
internalPlaybackThread = new PriorityHandlerThread("ExoPlayerImplInternal:Handler",
|
||||
Process.THREAD_PRIORITY_AUDIO);
|
||||
internalPlaybackThread.start();
|
||||
handler = new Handler(internalPlaybackThread.getLooper(), this);
|
||||
}
|
||||
|
||||
public Looper getPlaybackLooper() {
|
||||
return internalPlaybackThread.getLooper();
|
||||
}
|
||||
|
||||
public long getCurrentPosition() {
|
||||
return pendingSeekCount.get() > 0 ? lastSeekPositionMs : (positionUs / 1000);
|
||||
}
|
||||
|
||||
public long getBufferedPosition() {
|
||||
return bufferedPositionUs == TrackRenderer.UNKNOWN_TIME_US ? ExoPlayer.UNKNOWN_TIME
|
||||
: bufferedPositionUs / 1000;
|
||||
}
|
||||
|
||||
public long getDuration() {
|
||||
return durationUs == TrackRenderer.UNKNOWN_TIME_US ? ExoPlayer.UNKNOWN_TIME
|
||||
: durationUs / 1000;
|
||||
}
|
||||
|
||||
public void prepare(TrackRenderer... renderers) {
|
||||
handler.obtainMessage(MSG_PREPARE, renderers).sendToTarget();
|
||||
}
|
||||
|
||||
public void setPlayWhenReady(boolean playWhenReady) {
|
||||
handler.obtainMessage(MSG_SET_PLAY_WHEN_READY, playWhenReady ? 1 : 0, 0).sendToTarget();
|
||||
}
|
||||
|
||||
public void seekTo(long positionMs) {
|
||||
lastSeekPositionMs = positionMs;
|
||||
pendingSeekCount.incrementAndGet();
|
||||
handler.obtainMessage(MSG_SEEK_TO, Util.getTopInt(positionMs),
|
||||
Util.getBottomInt(positionMs)).sendToTarget();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
handler.sendEmptyMessage(MSG_STOP);
|
||||
}
|
||||
|
||||
public void setRendererSelectedTrack(int rendererIndex, int trackIndex) {
|
||||
handler.obtainMessage(MSG_SET_RENDERER_SELECTED_TRACK, rendererIndex, trackIndex)
|
||||
.sendToTarget();
|
||||
}
|
||||
|
||||
public void sendMessage(ExoPlayerComponent target, int messageType, Object message) {
|
||||
customMessagesSent++;
|
||||
handler.obtainMessage(MSG_CUSTOM, messageType, 0, Pair.create(target, message)).sendToTarget();
|
||||
}
|
||||
|
||||
public synchronized void blockingSendMessage(ExoPlayerComponent target, int messageType,
|
||||
Object message) {
|
||||
if (released) {
|
||||
Log.w(TAG, "Sent message(" + messageType + ") after release. Message ignored.");
|
||||
return;
|
||||
}
|
||||
int messageNumber = customMessagesSent++;
|
||||
handler.obtainMessage(MSG_CUSTOM, messageType, 0, Pair.create(target, message)).sendToTarget();
|
||||
while (customMessagesProcessed <= messageNumber) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void release() {
|
||||
if (released) {
|
||||
return;
|
||||
}
|
||||
handler.sendEmptyMessage(MSG_RELEASE);
|
||||
while (!released) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
internalPlaybackThread.quit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleMessage(Message msg) {
|
||||
try {
|
||||
switch (msg.what) {
|
||||
case MSG_PREPARE: {
|
||||
prepareInternal((TrackRenderer[]) msg.obj);
|
||||
return true;
|
||||
}
|
||||
case MSG_INCREMENTAL_PREPARE: {
|
||||
incrementalPrepareInternal();
|
||||
return true;
|
||||
}
|
||||
case MSG_SET_PLAY_WHEN_READY: {
|
||||
setPlayWhenReadyInternal(msg.arg1 != 0);
|
||||
return true;
|
||||
}
|
||||
case MSG_DO_SOME_WORK: {
|
||||
doSomeWork();
|
||||
return true;
|
||||
}
|
||||
case MSG_SEEK_TO: {
|
||||
seekToInternal(Util.getLong(msg.arg1, msg.arg2));
|
||||
return true;
|
||||
}
|
||||
case MSG_STOP: {
|
||||
stopInternal();
|
||||
return true;
|
||||
}
|
||||
case MSG_RELEASE: {
|
||||
releaseInternal();
|
||||
return true;
|
||||
}
|
||||
case MSG_CUSTOM: {
|
||||
sendMessageInternal(msg.arg1, msg.obj);
|
||||
return true;
|
||||
}
|
||||
case MSG_SET_RENDERER_SELECTED_TRACK: {
|
||||
setRendererSelectedTrackInternal(msg.arg1, msg.arg2);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} catch (ExoPlaybackException e) {
|
||||
Log.e(TAG, "Internal track renderer error.", e);
|
||||
eventHandler.obtainMessage(MSG_ERROR, e).sendToTarget();
|
||||
stopInternal();
|
||||
return true;
|
||||
} catch (RuntimeException e) {
|
||||
Log.e(TAG, "Internal runtime error.", e);
|
||||
eventHandler.obtainMessage(MSG_ERROR, new ExoPlaybackException(e, true)).sendToTarget();
|
||||
stopInternal();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void setState(int state) {
|
||||
if (this.state != state) {
|
||||
this.state = state;
|
||||
eventHandler.obtainMessage(MSG_STATE_CHANGED, state, 0).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareInternal(TrackRenderer[] renderers) throws ExoPlaybackException {
|
||||
resetInternal();
|
||||
this.renderers = renderers;
|
||||
Arrays.fill(trackFormats, null);
|
||||
setState(ExoPlayer.STATE_PREPARING);
|
||||
incrementalPrepareInternal();
|
||||
}
|
||||
|
||||
private void incrementalPrepareInternal() throws ExoPlaybackException {
|
||||
long operationStartTimeMs = SystemClock.elapsedRealtime();
|
||||
boolean prepared = true;
|
||||
for (int rendererIndex = 0; rendererIndex < renderers.length; rendererIndex++) {
|
||||
TrackRenderer renderer = renderers[rendererIndex];
|
||||
if (renderer.getState() == TrackRenderer.STATE_UNPREPARED) {
|
||||
int state = renderer.prepare(positionUs);
|
||||
if (state == TrackRenderer.STATE_UNPREPARED) {
|
||||
renderer.maybeThrowError();
|
||||
prepared = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!prepared) {
|
||||
// We're still waiting for some sources to be prepared.
|
||||
scheduleNextOperation(MSG_INCREMENTAL_PREPARE, operationStartTimeMs, PREPARE_INTERVAL_MS);
|
||||
return;
|
||||
}
|
||||
|
||||
long durationUs = 0;
|
||||
boolean allRenderersEnded = true;
|
||||
boolean allRenderersReadyOrEnded = true;
|
||||
for (int rendererIndex = 0; rendererIndex < renderers.length; rendererIndex++) {
|
||||
TrackRenderer renderer = renderers[rendererIndex];
|
||||
int rendererTrackCount = renderer.getTrackCount();
|
||||
MediaFormat[] rendererTrackFormats = new MediaFormat[rendererTrackCount];
|
||||
for (int trackIndex = 0; trackIndex < rendererTrackCount; trackIndex++) {
|
||||
rendererTrackFormats[trackIndex] = renderer.getFormat(trackIndex);
|
||||
}
|
||||
trackFormats[rendererIndex] = rendererTrackFormats;
|
||||
if (rendererTrackCount > 0) {
|
||||
if (durationUs == TrackRenderer.UNKNOWN_TIME_US) {
|
||||
// We've already encountered a track for which the duration is unknown, so the media
|
||||
// duration is unknown regardless of the duration of this track.
|
||||
} else {
|
||||
long trackDurationUs = renderer.getDurationUs();
|
||||
if (trackDurationUs == TrackRenderer.UNKNOWN_TIME_US) {
|
||||
durationUs = TrackRenderer.UNKNOWN_TIME_US;
|
||||
} else if (trackDurationUs == TrackRenderer.MATCH_LONGEST_US) {
|
||||
// Do nothing.
|
||||
} else {
|
||||
durationUs = Math.max(durationUs, trackDurationUs);
|
||||
}
|
||||
}
|
||||
int trackIndex = selectedTrackIndices[rendererIndex];
|
||||
if (0 <= trackIndex && trackIndex < rendererTrackFormats.length) {
|
||||
enableRenderer(renderer, trackIndex, false);
|
||||
allRenderersEnded = allRenderersEnded && renderer.isEnded();
|
||||
allRenderersReadyOrEnded = allRenderersReadyOrEnded && rendererReadyOrEnded(renderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.durationUs = durationUs;
|
||||
|
||||
if (allRenderersEnded
|
||||
&& (durationUs == TrackRenderer.UNKNOWN_TIME_US || durationUs <= positionUs)) {
|
||||
// We don't expect this case, but handle it anyway.
|
||||
state = ExoPlayer.STATE_ENDED;
|
||||
} else {
|
||||
state = allRenderersReadyOrEnded ? ExoPlayer.STATE_READY : ExoPlayer.STATE_BUFFERING;
|
||||
}
|
||||
|
||||
// Fire an event indicating that the player has been prepared, passing the initial state and
|
||||
// renderer track information.
|
||||
eventHandler.obtainMessage(MSG_PREPARED, state, 0, trackFormats).sendToTarget();
|
||||
|
||||
// Start the renderers if required, and schedule the first piece of work.
|
||||
if (playWhenReady && state == ExoPlayer.STATE_READY) {
|
||||
startRenderers();
|
||||
}
|
||||
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
||||
}
|
||||
|
||||
private void enableRenderer(TrackRenderer renderer, int trackIndex, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
renderer.enable(trackIndex, positionUs, joining);
|
||||
enabledRenderers.add(renderer);
|
||||
MediaClock mediaClock = renderer.getMediaClock();
|
||||
if (mediaClock != null) {
|
||||
Assertions.checkState(rendererMediaClock == null);
|
||||
rendererMediaClock = mediaClock;
|
||||
rendererMediaClockSource = renderer;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean rendererReadyOrEnded(TrackRenderer renderer) {
|
||||
if (renderer.isEnded()) {
|
||||
return true;
|
||||
}
|
||||
if (!renderer.isReady()) {
|
||||
return false;
|
||||
}
|
||||
if (state == ExoPlayer.STATE_READY) {
|
||||
return true;
|
||||
}
|
||||
long rendererDurationUs = renderer.getDurationUs();
|
||||
long rendererBufferedPositionUs = renderer.getBufferedPositionUs();
|
||||
long minBufferDurationUs = rebuffering ? minRebufferUs : minBufferUs;
|
||||
return minBufferDurationUs <= 0
|
||||
|| rendererBufferedPositionUs == TrackRenderer.UNKNOWN_TIME_US
|
||||
|| rendererBufferedPositionUs == TrackRenderer.END_OF_TRACK_US
|
||||
|| rendererBufferedPositionUs >= positionUs + minBufferDurationUs
|
||||
|| (rendererDurationUs != TrackRenderer.UNKNOWN_TIME_US
|
||||
&& rendererDurationUs != TrackRenderer.MATCH_LONGEST_US
|
||||
&& rendererBufferedPositionUs >= rendererDurationUs);
|
||||
}
|
||||
|
||||
private void setPlayWhenReadyInternal(boolean playWhenReady) throws ExoPlaybackException {
|
||||
try {
|
||||
rebuffering = false;
|
||||
this.playWhenReady = playWhenReady;
|
||||
if (!playWhenReady) {
|
||||
stopRenderers();
|
||||
updatePositionUs();
|
||||
} else {
|
||||
if (state == ExoPlayer.STATE_READY) {
|
||||
startRenderers();
|
||||
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
||||
} else if (state == ExoPlayer.STATE_BUFFERING) {
|
||||
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
eventHandler.obtainMessage(MSG_SET_PLAY_WHEN_READY_ACK).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private void startRenderers() throws ExoPlaybackException {
|
||||
rebuffering = false;
|
||||
standaloneMediaClock.start();
|
||||
for (int i = 0; i < enabledRenderers.size(); i++) {
|
||||
enabledRenderers.get(i).start();
|
||||
}
|
||||
}
|
||||
|
||||
private void stopRenderers() throws ExoPlaybackException {
|
||||
standaloneMediaClock.stop();
|
||||
for (int i = 0; i < enabledRenderers.size(); i++) {
|
||||
ensureStopped(enabledRenderers.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePositionUs() {
|
||||
if (rendererMediaClock != null && enabledRenderers.contains(rendererMediaClockSource)
|
||||
&& !rendererMediaClockSource.isEnded()) {
|
||||
positionUs = rendererMediaClock.getPositionUs();
|
||||
standaloneMediaClock.setPositionUs(positionUs);
|
||||
} else {
|
||||
positionUs = standaloneMediaClock.getPositionUs();
|
||||
}
|
||||
elapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000;
|
||||
}
|
||||
|
||||
private void doSomeWork() throws ExoPlaybackException {
|
||||
TraceUtil.beginSection("doSomeWork");
|
||||
long operationStartTimeMs = SystemClock.elapsedRealtime();
|
||||
long bufferedPositionUs = durationUs != TrackRenderer.UNKNOWN_TIME_US ? durationUs
|
||||
: Long.MAX_VALUE;
|
||||
boolean allRenderersEnded = true;
|
||||
boolean allRenderersReadyOrEnded = true;
|
||||
updatePositionUs();
|
||||
for (int i = 0; i < enabledRenderers.size(); i++) {
|
||||
TrackRenderer renderer = enabledRenderers.get(i);
|
||||
// TODO: Each renderer should return the maximum delay before which it wishes to be
|
||||
// invoked again. The minimum of these values should then be used as the delay before the next
|
||||
// invocation of this method.
|
||||
renderer.doSomeWork(positionUs, elapsedRealtimeUs);
|
||||
allRenderersEnded = allRenderersEnded && renderer.isEnded();
|
||||
|
||||
// Determine whether the renderer is ready (or ended). If it's not, throw an error that's
|
||||
// preventing the renderer from making progress, if such an error exists.
|
||||
boolean rendererReadyOrEnded = rendererReadyOrEnded(renderer);
|
||||
if (!rendererReadyOrEnded) {
|
||||
renderer.maybeThrowError();
|
||||
}
|
||||
allRenderersReadyOrEnded = allRenderersReadyOrEnded && rendererReadyOrEnded;
|
||||
|
||||
if (bufferedPositionUs == TrackRenderer.UNKNOWN_TIME_US) {
|
||||
// We've already encountered a track for which the buffered position is unknown. Hence the
|
||||
// media buffer position unknown regardless of the buffered position of this track.
|
||||
} else {
|
||||
long rendererDurationUs = renderer.getDurationUs();
|
||||
long rendererBufferedPositionUs = renderer.getBufferedPositionUs();
|
||||
if (rendererBufferedPositionUs == TrackRenderer.UNKNOWN_TIME_US) {
|
||||
bufferedPositionUs = TrackRenderer.UNKNOWN_TIME_US;
|
||||
} else if (rendererBufferedPositionUs == TrackRenderer.END_OF_TRACK_US
|
||||
|| (rendererDurationUs != TrackRenderer.UNKNOWN_TIME_US
|
||||
&& rendererDurationUs != TrackRenderer.MATCH_LONGEST_US
|
||||
&& rendererBufferedPositionUs >= rendererDurationUs)) {
|
||||
// This track is fully buffered.
|
||||
} else {
|
||||
bufferedPositionUs = Math.min(bufferedPositionUs, rendererBufferedPositionUs);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.bufferedPositionUs = bufferedPositionUs;
|
||||
|
||||
if (allRenderersEnded
|
||||
&& (durationUs == TrackRenderer.UNKNOWN_TIME_US || durationUs <= positionUs)) {
|
||||
setState(ExoPlayer.STATE_ENDED);
|
||||
stopRenderers();
|
||||
} else if (state == ExoPlayer.STATE_BUFFERING && allRenderersReadyOrEnded) {
|
||||
setState(ExoPlayer.STATE_READY);
|
||||
if (playWhenReady) {
|
||||
startRenderers();
|
||||
}
|
||||
} else if (state == ExoPlayer.STATE_READY && !allRenderersReadyOrEnded) {
|
||||
rebuffering = playWhenReady;
|
||||
setState(ExoPlayer.STATE_BUFFERING);
|
||||
stopRenderers();
|
||||
}
|
||||
|
||||
handler.removeMessages(MSG_DO_SOME_WORK);
|
||||
if ((playWhenReady && state == ExoPlayer.STATE_READY) || state == ExoPlayer.STATE_BUFFERING) {
|
||||
scheduleNextOperation(MSG_DO_SOME_WORK, operationStartTimeMs, RENDERING_INTERVAL_MS);
|
||||
} else if (!enabledRenderers.isEmpty()) {
|
||||
scheduleNextOperation(MSG_DO_SOME_WORK, operationStartTimeMs, IDLE_INTERVAL_MS);
|
||||
}
|
||||
|
||||
TraceUtil.endSection();
|
||||
}
|
||||
|
||||
private void scheduleNextOperation(int operationType, long thisOperationStartTimeMs,
|
||||
long intervalMs) {
|
||||
long nextOperationStartTimeMs = thisOperationStartTimeMs + intervalMs;
|
||||
long nextOperationDelayMs = nextOperationStartTimeMs - SystemClock.elapsedRealtime();
|
||||
if (nextOperationDelayMs <= 0) {
|
||||
handler.sendEmptyMessage(operationType);
|
||||
} else {
|
||||
handler.sendEmptyMessageDelayed(operationType, nextOperationDelayMs);
|
||||
}
|
||||
}
|
||||
|
||||
private void seekToInternal(long positionMs) throws ExoPlaybackException {
|
||||
try {
|
||||
if (positionMs == (positionUs / 1000)) {
|
||||
// Seek is to the current position. Do nothing.
|
||||
return;
|
||||
}
|
||||
|
||||
rebuffering = false;
|
||||
positionUs = positionMs * 1000;
|
||||
standaloneMediaClock.stop();
|
||||
standaloneMediaClock.setPositionUs(positionUs);
|
||||
if (state == ExoPlayer.STATE_IDLE || state == ExoPlayer.STATE_PREPARING) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < enabledRenderers.size(); i++) {
|
||||
TrackRenderer renderer = enabledRenderers.get(i);
|
||||
ensureStopped(renderer);
|
||||
renderer.seekTo(positionUs);
|
||||
}
|
||||
setState(ExoPlayer.STATE_BUFFERING);
|
||||
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
||||
} finally {
|
||||
pendingSeekCount.decrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
private void stopInternal() {
|
||||
resetInternal();
|
||||
setState(ExoPlayer.STATE_IDLE);
|
||||
}
|
||||
|
||||
private void releaseInternal() {
|
||||
resetInternal();
|
||||
setState(ExoPlayer.STATE_IDLE);
|
||||
synchronized (this) {
|
||||
released = true;
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
private void resetInternal() {
|
||||
handler.removeMessages(MSG_DO_SOME_WORK);
|
||||
handler.removeMessages(MSG_INCREMENTAL_PREPARE);
|
||||
rebuffering = false;
|
||||
standaloneMediaClock.stop();
|
||||
if (renderers == null) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < renderers.length; i++) {
|
||||
TrackRenderer renderer = renderers[i];
|
||||
stopAndDisable(renderer);
|
||||
release(renderer);
|
||||
}
|
||||
renderers = null;
|
||||
rendererMediaClock = null;
|
||||
rendererMediaClockSource = null;
|
||||
enabledRenderers.clear();
|
||||
}
|
||||
|
||||
private void stopAndDisable(TrackRenderer renderer) {
|
||||
try {
|
||||
ensureDisabled(renderer);
|
||||
} catch (ExoPlaybackException e) {
|
||||
// There's nothing we can do.
|
||||
Log.e(TAG, "Stop failed.", e);
|
||||
} catch (RuntimeException e) {
|
||||
// Ditto.
|
||||
Log.e(TAG, "Stop failed.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void release(TrackRenderer renderer) {
|
||||
try {
|
||||
renderer.release();
|
||||
} catch (ExoPlaybackException e) {
|
||||
// There's nothing we can do.
|
||||
Log.e(TAG, "Release failed.", e);
|
||||
} catch (RuntimeException e) {
|
||||
// Ditto.
|
||||
Log.e(TAG, "Release failed.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> void sendMessageInternal(int what, Object obj)
|
||||
throws ExoPlaybackException {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Pair<ExoPlayerComponent, Object> targetAndMessage = (Pair<ExoPlayerComponent, Object>) obj;
|
||||
targetAndMessage.first.handleMessage(what, targetAndMessage.second);
|
||||
if (state != ExoPlayer.STATE_IDLE && state != ExoPlayer.STATE_PREPARING) {
|
||||
// The message may have caused something to change that now requires us to do work.
|
||||
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
||||
}
|
||||
} finally {
|
||||
synchronized (this) {
|
||||
customMessagesProcessed++;
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setRendererSelectedTrackInternal(int rendererIndex, int trackIndex)
|
||||
throws ExoPlaybackException {
|
||||
if (selectedTrackIndices[rendererIndex] == trackIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
selectedTrackIndices[rendererIndex] = trackIndex;
|
||||
if (state == ExoPlayer.STATE_IDLE || state == ExoPlayer.STATE_PREPARING) {
|
||||
return;
|
||||
}
|
||||
|
||||
TrackRenderer renderer = renderers[rendererIndex];
|
||||
int rendererState = renderer.getState();
|
||||
if (rendererState == TrackRenderer.STATE_UNPREPARED
|
||||
|| rendererState == TrackRenderer.STATE_RELEASED
|
||||
|| renderer.getTrackCount() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isEnabled = rendererState == TrackRenderer.STATE_ENABLED
|
||||
|| rendererState == TrackRenderer.STATE_STARTED;
|
||||
boolean shouldEnable = 0 <= trackIndex && trackIndex < trackFormats[rendererIndex].length;
|
||||
|
||||
if (isEnabled) {
|
||||
// The renderer is currently enabled. We need to disable it, so that we can either re-enable
|
||||
// it with the newly selected track (if shouldEnable is true) or because we want to leave it
|
||||
// disabled (if shouldEnable is false).
|
||||
if (!shouldEnable && renderer == rendererMediaClockSource) {
|
||||
// We've been using rendererMediaClockSource to advance the current position, but it's being
|
||||
// disabled and won't be re-enabled. Sync standaloneMediaClock so that it can take over
|
||||
// timing responsibilities.
|
||||
standaloneMediaClock.setPositionUs(rendererMediaClock.getPositionUs());
|
||||
}
|
||||
ensureDisabled(renderer);
|
||||
enabledRenderers.remove(renderer);
|
||||
}
|
||||
|
||||
if (shouldEnable) {
|
||||
// Re-enable the renderer with the newly selected track.
|
||||
boolean playing = playWhenReady && state == ExoPlayer.STATE_READY;
|
||||
// Consider as joining if the renderer was previously disabled, but not when switching tracks.
|
||||
boolean joining = !isEnabled && playing;
|
||||
enableRenderer(renderer, trackIndex, joining);
|
||||
if (playing) {
|
||||
renderer.start();
|
||||
}
|
||||
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureStopped(TrackRenderer renderer) throws ExoPlaybackException {
|
||||
if (renderer.getState() == TrackRenderer.STATE_STARTED) {
|
||||
renderer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureDisabled(TrackRenderer renderer) throws ExoPlaybackException {
|
||||
ensureStopped(renderer);
|
||||
if (renderer.getState() == TrackRenderer.STATE_ENABLED) {
|
||||
renderer.disable();
|
||||
if (renderer == rendererMediaClockSource) {
|
||||
rendererMediaClock = null;
|
||||
rendererMediaClockSource = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
/**
|
||||
* Information about the ExoPlayer library.
|
||||
*/
|
||||
public final class ExoPlayerLibraryInfo {
|
||||
|
||||
/**
|
||||
* The version of the library, expressed as a string.
|
||||
*/
|
||||
public static final String VERSION = "1.5.8";
|
||||
|
||||
/**
|
||||
* The version of the library, expressed as an integer.
|
||||
* <p>
|
||||
* Three digits are used for each component of {@link #VERSION}. For example "1.2.3" has the
|
||||
* corresponding integer version 1002003 (001-002-003), and "123.45.6" has the corresponding
|
||||
* integer version 123045006 (123-045-006).
|
||||
*/
|
||||
public static final int VERSION_INT = 1005008;
|
||||
|
||||
/**
|
||||
* Whether the library was compiled with {@link org.telegram.messenger.exoplayer.util.Assertions}
|
||||
* checks enabled.
|
||||
*/
|
||||
public static final boolean ASSERTIONS_ENABLED = true;
|
||||
|
||||
/**
|
||||
* Whether the library was compiled with {@link org.telegram.messenger.exoplayer.util.TraceUtil}
|
||||
* trace enabled.
|
||||
*/
|
||||
public static final boolean TRACE_ENABLED = true;
|
||||
|
||||
private ExoPlayerLibraryInfo() {}
|
||||
|
||||
}
|
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import org.telegram.messenger.exoplayer.SampleSource.SampleSourceReader;
|
||||
import org.telegram.messenger.exoplayer.drm.DrmInitData;
|
||||
import org.telegram.messenger.exoplayer.drm.DrmInitData.SchemeInitData;
|
||||
import org.telegram.messenger.exoplayer.extractor.ExtractorSampleSource;
|
||||
import org.telegram.messenger.exoplayer.extractor.mp4.PsshAtomUtil;
|
||||
import org.telegram.messenger.exoplayer.util.Assertions;
|
||||
import org.telegram.messenger.exoplayer.util.MimeTypes;
|
||||
import org.telegram.messenger.exoplayer.util.Util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.media.MediaExtractor;
|
||||
import android.net.Uri;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Extracts samples from a stream using Android's {@link MediaExtractor}.
|
||||
* <p>
|
||||
* Warning - This class is marked as deprecated because there are known device specific issues
|
||||
* associated with its use, including playbacks not starting, playbacks stuttering and other
|
||||
* miscellaneous failures. For mp4, m4a, mp3, webm, mkv, mpeg-ts, ogg, wav and aac playbacks it is
|
||||
* strongly recommended to use {@link ExtractorSampleSource} instead. Where this is not possible
|
||||
* this class can still be used, but please be aware of the associated risks. Playing container
|
||||
* formats for which an ExoPlayer extractor does not yet exist (e.g. avi) is a valid use case of
|
||||
* this class.
|
||||
* <p>
|
||||
* Over time we hope to enhance {@link ExtractorSampleSource} to support more formats, and hence
|
||||
* make use of this class unnecessary.
|
||||
*/
|
||||
// TODO: This implementation needs to be fixed so that its methods are non-blocking (either
|
||||
// through use of a background thread, or through changes to the framework's MediaExtractor API).
|
||||
@Deprecated
|
||||
@TargetApi(16)
|
||||
public final class FrameworkSampleSource implements SampleSource, SampleSourceReader {
|
||||
|
||||
private static final int ALLOWED_FLAGS_MASK = C.SAMPLE_FLAG_SYNC | C.SAMPLE_FLAG_ENCRYPTED;
|
||||
|
||||
private static final int TRACK_STATE_DISABLED = 0;
|
||||
private static final int TRACK_STATE_ENABLED = 1;
|
||||
private static final int TRACK_STATE_FORMAT_SENT = 2;
|
||||
|
||||
// Parameters for a Uri data source.
|
||||
private final Context context;
|
||||
private final Uri uri;
|
||||
private final Map<String, String> headers;
|
||||
|
||||
// Parameters for a FileDescriptor data source.
|
||||
private final FileDescriptor fileDescriptor;
|
||||
private final long fileDescriptorOffset;
|
||||
private final long fileDescriptorLength;
|
||||
|
||||
private IOException preparationError;
|
||||
private MediaExtractor extractor;
|
||||
private MediaFormat[] trackFormats;
|
||||
private boolean prepared;
|
||||
private int remainingReleaseCount;
|
||||
private int[] trackStates;
|
||||
private boolean[] pendingDiscontinuities;
|
||||
|
||||
private long lastSeekPositionUs;
|
||||
private long pendingSeekPositionUs;
|
||||
|
||||
/**
|
||||
* Instantiates a new sample extractor reading from the specified {@code uri}.
|
||||
*
|
||||
* @param context Context for resolving {@code uri}.
|
||||
* @param uri The content URI from which to extract data.
|
||||
* @param headers Headers to send with requests for data.
|
||||
*/
|
||||
public FrameworkSampleSource(Context context, Uri uri, Map<String, String> headers) {
|
||||
Assertions.checkState(Util.SDK_INT >= 16);
|
||||
this.context = Assertions.checkNotNull(context);
|
||||
this.uri = Assertions.checkNotNull(uri);
|
||||
this.headers = headers;
|
||||
fileDescriptor = null;
|
||||
fileDescriptorOffset = 0;
|
||||
fileDescriptorLength = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new sample extractor reading from the specified seekable {@code fileDescriptor}.
|
||||
* The caller is responsible for releasing the file descriptor.
|
||||
*
|
||||
* @param fileDescriptor File descriptor from which to read.
|
||||
* @param fileDescriptorOffset The offset in bytes where the data to be extracted starts.
|
||||
* @param fileDescriptorLength The length in bytes of the data to be extracted.
|
||||
*/
|
||||
public FrameworkSampleSource(FileDescriptor fileDescriptor, long fileDescriptorOffset,
|
||||
long fileDescriptorLength) {
|
||||
Assertions.checkState(Util.SDK_INT >= 16);
|
||||
this.fileDescriptor = Assertions.checkNotNull(fileDescriptor);
|
||||
this.fileDescriptorOffset = fileDescriptorOffset;
|
||||
this.fileDescriptorLength = fileDescriptorLength;
|
||||
context = null;
|
||||
uri = null;
|
||||
headers = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SampleSourceReader register() {
|
||||
remainingReleaseCount++;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean prepare(long positionUs) {
|
||||
if (!prepared) {
|
||||
if (preparationError != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
extractor = new MediaExtractor();
|
||||
try {
|
||||
if (context != null) {
|
||||
extractor.setDataSource(context, uri, headers);
|
||||
} else {
|
||||
extractor.setDataSource(fileDescriptor, fileDescriptorOffset, fileDescriptorLength);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
preparationError = e;
|
||||
return false;
|
||||
}
|
||||
|
||||
trackStates = new int[extractor.getTrackCount()];
|
||||
pendingDiscontinuities = new boolean[trackStates.length];
|
||||
trackFormats = new MediaFormat[trackStates.length];
|
||||
for (int i = 0; i < trackStates.length; i++) {
|
||||
trackFormats[i] = createMediaFormat(extractor.getTrackFormat(i));
|
||||
}
|
||||
prepared = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTrackCount() {
|
||||
Assertions.checkState(prepared);
|
||||
return trackStates.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaFormat getFormat(int track) {
|
||||
Assertions.checkState(prepared);
|
||||
return trackFormats[track];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable(int track, long positionUs) {
|
||||
Assertions.checkState(prepared);
|
||||
Assertions.checkState(trackStates[track] == TRACK_STATE_DISABLED);
|
||||
trackStates[track] = TRACK_STATE_ENABLED;
|
||||
extractor.selectTrack(track);
|
||||
seekToUsInternal(positionUs, positionUs != 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean continueBuffering(int track, long positionUs) {
|
||||
// MediaExtractor takes care of buffering and blocks until it has samples, so we can always
|
||||
// return true here. Although note that the blocking behavior is itself as bug, as per the
|
||||
// TODO further up this file. This method will need to return something else as part of fixing
|
||||
// the TODO.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readDiscontinuity(int track) {
|
||||
if (pendingDiscontinuities[track]) {
|
||||
pendingDiscontinuities[track] = false;
|
||||
return lastSeekPositionUs;
|
||||
}
|
||||
return NO_DISCONTINUITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readData(int track, long positionUs, MediaFormatHolder formatHolder,
|
||||
SampleHolder sampleHolder) {
|
||||
Assertions.checkState(prepared);
|
||||
Assertions.checkState(trackStates[track] != TRACK_STATE_DISABLED);
|
||||
if (pendingDiscontinuities[track]) {
|
||||
return NOTHING_READ;
|
||||
}
|
||||
if (trackStates[track] != TRACK_STATE_FORMAT_SENT) {
|
||||
formatHolder.format = trackFormats[track];
|
||||
formatHolder.drmInitData = Util.SDK_INT >= 18 ? getDrmInitDataV18() : null;
|
||||
trackStates[track] = TRACK_STATE_FORMAT_SENT;
|
||||
return FORMAT_READ;
|
||||
}
|
||||
int extractorTrackIndex = extractor.getSampleTrackIndex();
|
||||
if (extractorTrackIndex == track) {
|
||||
if (sampleHolder.data != null) {
|
||||
int offset = sampleHolder.data.position();
|
||||
sampleHolder.size = extractor.readSampleData(sampleHolder.data, offset);
|
||||
sampleHolder.data.position(offset + sampleHolder.size);
|
||||
} else {
|
||||
sampleHolder.size = 0;
|
||||
}
|
||||
sampleHolder.timeUs = extractor.getSampleTime();
|
||||
sampleHolder.flags = extractor.getSampleFlags() & ALLOWED_FLAGS_MASK;
|
||||
if (sampleHolder.isEncrypted()) {
|
||||
sampleHolder.cryptoInfo.setFromExtractorV16(extractor);
|
||||
}
|
||||
pendingSeekPositionUs = C.UNKNOWN_TIME_US;
|
||||
extractor.advance();
|
||||
return SAMPLE_READ;
|
||||
} else {
|
||||
return extractorTrackIndex < 0 ? END_OF_STREAM : NOTHING_READ;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable(int track) {
|
||||
Assertions.checkState(prepared);
|
||||
Assertions.checkState(trackStates[track] != TRACK_STATE_DISABLED);
|
||||
extractor.unselectTrack(track);
|
||||
pendingDiscontinuities[track] = false;
|
||||
trackStates[track] = TRACK_STATE_DISABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeThrowError() throws IOException {
|
||||
if (preparationError != null) {
|
||||
throw preparationError;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekToUs(long positionUs) {
|
||||
Assertions.checkState(prepared);
|
||||
seekToUsInternal(positionUs, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getBufferedPositionUs() {
|
||||
Assertions.checkState(prepared);
|
||||
long bufferedDurationUs = extractor.getCachedDuration();
|
||||
if (bufferedDurationUs == -1) {
|
||||
return TrackRenderer.UNKNOWN_TIME_US;
|
||||
} else {
|
||||
long sampleTime = extractor.getSampleTime();
|
||||
return sampleTime == -1 ? TrackRenderer.END_OF_TRACK_US : sampleTime + bufferedDurationUs;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
Assertions.checkState(remainingReleaseCount > 0);
|
||||
if (--remainingReleaseCount == 0 && extractor != null) {
|
||||
extractor.release();
|
||||
extractor = null;
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(18)
|
||||
private DrmInitData getDrmInitDataV18() {
|
||||
// MediaExtractor only supports psshInfo for MP4, so it's ok to hard code the mimeType here.
|
||||
Map<UUID, byte[]> psshInfo = extractor.getPsshInfo();
|
||||
if (psshInfo == null || psshInfo.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
DrmInitData.Mapped drmInitData = new DrmInitData.Mapped();
|
||||
for (UUID uuid : psshInfo.keySet()) {
|
||||
byte[] psshAtom = PsshAtomUtil.buildPsshAtom(uuid, psshInfo.get(uuid));
|
||||
drmInitData.put(uuid, new SchemeInitData(MimeTypes.VIDEO_MP4, psshAtom));
|
||||
}
|
||||
return drmInitData;
|
||||
}
|
||||
|
||||
private void seekToUsInternal(long positionUs, boolean force) {
|
||||
// Unless forced, avoid duplicate calls to the underlying extractor's seek method in the case
|
||||
// that there have been no interleaving calls to readSample.
|
||||
if (force || pendingSeekPositionUs != positionUs) {
|
||||
lastSeekPositionUs = positionUs;
|
||||
pendingSeekPositionUs = positionUs;
|
||||
extractor.seekTo(positionUs, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
|
||||
for (int i = 0; i < trackStates.length; ++i) {
|
||||
if (trackStates[i] != TRACK_STATE_DISABLED) {
|
||||
pendingDiscontinuities[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
private static MediaFormat createMediaFormat(android.media.MediaFormat format) {
|
||||
String mimeType = format.getString(android.media.MediaFormat.KEY_MIME);
|
||||
String language = getOptionalStringV16(format, android.media.MediaFormat.KEY_LANGUAGE);
|
||||
int maxInputSize = getOptionalIntegerV16(format, android.media.MediaFormat.KEY_MAX_INPUT_SIZE);
|
||||
int width = getOptionalIntegerV16(format, android.media.MediaFormat.KEY_WIDTH);
|
||||
int height = getOptionalIntegerV16(format, android.media.MediaFormat.KEY_HEIGHT);
|
||||
int rotationDegrees = getOptionalIntegerV16(format, "rotation-degrees");
|
||||
int channelCount = getOptionalIntegerV16(format, android.media.MediaFormat.KEY_CHANNEL_COUNT);
|
||||
int sampleRate = getOptionalIntegerV16(format, android.media.MediaFormat.KEY_SAMPLE_RATE);
|
||||
int encoderDelay = getOptionalIntegerV16(format, "encoder-delay");
|
||||
int encoderPadding = getOptionalIntegerV16(format, "encoder-padding");
|
||||
ArrayList<byte[]> initializationData = new ArrayList<>();
|
||||
for (int i = 0; format.containsKey("csd-" + i); i++) {
|
||||
ByteBuffer buffer = format.getByteBuffer("csd-" + i);
|
||||
byte[] data = new byte[buffer.limit()];
|
||||
buffer.get(data);
|
||||
initializationData.add(data);
|
||||
buffer.flip();
|
||||
}
|
||||
long durationUs = format.containsKey(android.media.MediaFormat.KEY_DURATION)
|
||||
? format.getLong(android.media.MediaFormat.KEY_DURATION) : C.UNKNOWN_TIME_US;
|
||||
int pcmEncoding = MimeTypes.AUDIO_RAW.equals(mimeType) ? C.ENCODING_PCM_16BIT
|
||||
: MediaFormat.NO_VALUE;
|
||||
MediaFormat mediaFormat = new MediaFormat(null, mimeType, MediaFormat.NO_VALUE, maxInputSize,
|
||||
durationUs, width, height, rotationDegrees, MediaFormat.NO_VALUE, channelCount, sampleRate,
|
||||
language, MediaFormat.OFFSET_SAMPLE_RELATIVE, initializationData, false,
|
||||
MediaFormat.NO_VALUE, MediaFormat.NO_VALUE, pcmEncoding, encoderDelay, encoderPadding);
|
||||
mediaFormat.setFrameworkFormatV16(format);
|
||||
return mediaFormat;
|
||||
}
|
||||
|
||||
@TargetApi(16)
|
||||
private static final String getOptionalStringV16(android.media.MediaFormat format, String key) {
|
||||
return format.containsKey(key) ? format.getString(key) : null;
|
||||
}
|
||||
|
||||
@TargetApi(16)
|
||||
private static final int getOptionalIntegerV16(android.media.MediaFormat format, String key) {
|
||||
return format.containsKey(key) ? format.getInteger(key) : MediaFormat.NO_VALUE;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import org.telegram.messenger.exoplayer.upstream.Allocator;
|
||||
|
||||
/**
|
||||
* Coordinates multiple loaders of time series data.
|
||||
*/
|
||||
public interface LoadControl {
|
||||
|
||||
/**
|
||||
* Registers a loader.
|
||||
*
|
||||
* @param loader The loader being registered.
|
||||
* @param bufferSizeContribution For instances whose {@link Allocator} maintains a pool of memory
|
||||
* for the purpose of satisfying allocation requests, this is a hint indicating the loader's
|
||||
* desired contribution to the size of the pool, in bytes.
|
||||
*/
|
||||
void register(Object loader, int bufferSizeContribution);
|
||||
|
||||
/**
|
||||
* Unregisters a loader.
|
||||
*
|
||||
* @param loader The loader being unregistered.
|
||||
*/
|
||||
void unregister(Object loader);
|
||||
|
||||
/**
|
||||
* Gets the {@link Allocator} that loaders should use to obtain memory allocations into which
|
||||
* data can be loaded.
|
||||
*
|
||||
* @return The {@link Allocator} to use.
|
||||
*/
|
||||
Allocator getAllocator();
|
||||
|
||||
/**
|
||||
* Hints to the control that it should consider trimming any unused memory being held in order
|
||||
* to satisfy allocation requests.
|
||||
* <p>
|
||||
* This method is typically invoked by a recently unregistered loader, once it has released all
|
||||
* of its allocations back to the {@link Allocator}.
|
||||
*/
|
||||
void trimAllocator();
|
||||
|
||||
/**
|
||||
* Invoked by a loader to update the control with its current state.
|
||||
* <p>
|
||||
* This method must be called by a registered loader whenever its state changes. This is true
|
||||
* even if the registered loader does not itself wish to start its next load (since the state of
|
||||
* the loader will still affect whether other registered loaders are allowed to proceed).
|
||||
*
|
||||
* @param loader The loader invoking the update.
|
||||
* @param playbackPositionUs The loader's playback position.
|
||||
* @param nextLoadPositionUs The loader's next load position. -1 if finished, failed, or if the
|
||||
* next load position is not yet known.
|
||||
* @param loading Whether the loader is currently loading data.
|
||||
* @return True if the loader is allowed to start its next load. False otherwise.
|
||||
*/
|
||||
boolean update(Object loader, long playbackPositionUs, long nextLoadPositionUs, boolean loading);
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -13,12 +13,16 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
package org.telegram.messenger.Animation;
|
||||
/**
|
||||
* Tracks the progression of media time.
|
||||
*/
|
||||
public interface MediaClock {
|
||||
|
||||
public class IntEvaluator implements TypeEvaluator<Integer> {
|
||||
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
|
||||
int startInt = startValue;
|
||||
return (int)(startInt + fraction * (endValue - startInt));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return The current media position in microseconds.
|
||||
*/
|
||||
long getPositionUs();
|
||||
|
||||
}
|
|
@ -0,0 +1,486 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import org.telegram.messenger.exoplayer.MediaCodecUtil.DecoderQueryException;
|
||||
import org.telegram.messenger.exoplayer.audio.AudioCapabilities;
|
||||
import org.telegram.messenger.exoplayer.audio.AudioTrack;
|
||||
import org.telegram.messenger.exoplayer.drm.DrmSessionManager;
|
||||
import org.telegram.messenger.exoplayer.util.MimeTypes;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaCodec;
|
||||
import android.media.PlaybackParams;
|
||||
import android.media.audiofx.Virtualizer;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Decodes and renders audio using {@link MediaCodec} and {@link android.media.AudioTrack}.
|
||||
*/
|
||||
@TargetApi(16)
|
||||
public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implements MediaClock {
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be notified of {@link MediaCodecAudioTrackRenderer}
|
||||
* events.
|
||||
*/
|
||||
public interface EventListener extends MediaCodecTrackRenderer.EventListener {
|
||||
|
||||
/**
|
||||
* Invoked when an {@link AudioTrack} fails to initialize.
|
||||
*
|
||||
* @param e The corresponding exception.
|
||||
*/
|
||||
void onAudioTrackInitializationError(AudioTrack.InitializationException e);
|
||||
|
||||
/**
|
||||
* Invoked when an {@link AudioTrack} write fails.
|
||||
*
|
||||
* @param e The corresponding exception.
|
||||
*/
|
||||
void onAudioTrackWriteError(AudioTrack.WriteException e);
|
||||
|
||||
/**
|
||||
* Invoked when an {@link AudioTrack} underrun occurs.
|
||||
*
|
||||
* @param bufferSize The size of the {@link AudioTrack}'s buffer, in bytes.
|
||||
* @param bufferSizeMs The size of the {@link AudioTrack}'s buffer, in milliseconds, if it is
|
||||
* configured for PCM output. -1 if it is configured for passthrough output, as the buffered
|
||||
* media can have a variable bitrate so the duration may be unknown.
|
||||
* @param elapsedSinceLastFeedMs The time since the {@link AudioTrack} was last fed data.
|
||||
*/
|
||||
void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of a message that can be passed to an instance of this class via
|
||||
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object
|
||||
* should be a {@link Float} with 0 being silence and 1 being unity gain.
|
||||
*/
|
||||
public static final int MSG_SET_VOLUME = 1;
|
||||
|
||||
/**
|
||||
* The type of a message that can be passed to an instance of this class via
|
||||
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object
|
||||
* should be a {@link android.media.PlaybackParams}, which will be used to configure the
|
||||
* underlying {@link android.media.AudioTrack}. The message object should not be modified by the
|
||||
* caller after it has been passed
|
||||
*/
|
||||
public static final int MSG_SET_PLAYBACK_PARAMS = 2;
|
||||
|
||||
private final EventListener eventListener;
|
||||
private final AudioTrack audioTrack;
|
||||
|
||||
private boolean passthroughEnabled;
|
||||
private android.media.MediaFormat passthroughMediaFormat;
|
||||
private int pcmEncoding;
|
||||
private int audioSessionId;
|
||||
private long currentPositionUs;
|
||||
private boolean allowPositionDiscontinuity;
|
||||
|
||||
private boolean audioTrackHasData;
|
||||
private long lastFeedElapsedRealtimeMs;
|
||||
|
||||
/**
|
||||
* @param source The upstream source from which the renderer obtains samples.
|
||||
* @param mediaCodecSelector A decoder selector.
|
||||
*/
|
||||
public MediaCodecAudioTrackRenderer(SampleSource source, MediaCodecSelector mediaCodecSelector) {
|
||||
this(source, mediaCodecSelector, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param source The upstream source from which the renderer obtains samples.
|
||||
* @param mediaCodecSelector A decoder selector.
|
||||
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted
|
||||
* content is not required.
|
||||
* @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions.
|
||||
* For example a media file may start with a short clear region so as to allow playback to
|
||||
* begin in parallel with key acquisition. This parameter specifies whether the renderer is
|
||||
* permitted to play clear regions of encrypted media files before {@code drmSessionManager}
|
||||
* has obtained the keys necessary to decrypt encrypted regions of the media.
|
||||
*/
|
||||
public MediaCodecAudioTrackRenderer(SampleSource source, MediaCodecSelector mediaCodecSelector,
|
||||
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys) {
|
||||
this(source, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param source The upstream source from which the renderer obtains samples.
|
||||
* @param mediaCodecSelector A decoder selector.
|
||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||
* null if delivery of events is not required.
|
||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||
*/
|
||||
public MediaCodecAudioTrackRenderer(SampleSource source, MediaCodecSelector mediaCodecSelector,
|
||||
Handler eventHandler, EventListener eventListener) {
|
||||
this(source, mediaCodecSelector, null, true, eventHandler, eventListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param source The upstream source from which the renderer obtains samples.
|
||||
* @param mediaCodecSelector A decoder selector.
|
||||
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted
|
||||
* content is not required.
|
||||
* @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions.
|
||||
* For example a media file may start with a short clear region so as to allow playback to
|
||||
* begin in parallel with key acquisition. This parameter specifies whether the renderer is
|
||||
* permitted to play clear regions of encrypted media files before {@code drmSessionManager}
|
||||
* has obtained the keys necessary to decrypt encrypted regions of the media.
|
||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||
* null if delivery of events is not required.
|
||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||
*/
|
||||
public MediaCodecAudioTrackRenderer(SampleSource source, MediaCodecSelector mediaCodecSelector,
|
||||
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys,
|
||||
Handler eventHandler, EventListener eventListener) {
|
||||
this(source, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler,
|
||||
eventListener, null, AudioManager.STREAM_MUSIC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param source The upstream source from which the renderer obtains samples.
|
||||
* @param mediaCodecSelector A decoder selector.
|
||||
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted
|
||||
* content is not required.
|
||||
* @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions.
|
||||
* For example a media file may start with a short clear region so as to allow playback to
|
||||
* begin in parallel with key acquisition. This parameter specifies whether the renderer is
|
||||
* permitted to play clear regions of encrypted media files before {@code drmSessionManager}
|
||||
* has obtained the keys necessary to decrypt encrypted regions of the media.
|
||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||
* null if delivery of events is not required.
|
||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||
* @param audioCapabilities The audio capabilities for playback on this device. May be null if the
|
||||
* default capabilities (no encoded audio passthrough support) should be assumed.
|
||||
* @param streamType The type of audio stream for the {@link AudioTrack}.
|
||||
*/
|
||||
public MediaCodecAudioTrackRenderer(SampleSource source, MediaCodecSelector mediaCodecSelector,
|
||||
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys,
|
||||
Handler eventHandler, EventListener eventListener, AudioCapabilities audioCapabilities,
|
||||
int streamType) {
|
||||
this (new SampleSource[] {source}, mediaCodecSelector, drmSessionManager,
|
||||
playClearSamplesWithoutKeys, eventHandler, eventListener, audioCapabilities, streamType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sources The upstream sources from which the renderer obtains samples.
|
||||
* @param mediaCodecSelector A decoder selector.
|
||||
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted
|
||||
* content is not required.
|
||||
* @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions.
|
||||
* For example a media file may start with a short clear region so as to allow playback to
|
||||
* begin in parallel with key acquisition. This parameter specifies whether the renderer is
|
||||
* permitted to play clear regions of encrypted media files before {@code drmSessionManager}
|
||||
* has obtained the keys necessary to decrypt encrypted regions of the media.
|
||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||
* null if delivery of events is not required.
|
||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||
* @param audioCapabilities The audio capabilities for playback on this device. May be null if the
|
||||
* default capabilities (no encoded audio passthrough support) should be assumed.
|
||||
* @param streamType The type of audio stream for the {@link AudioTrack}.
|
||||
*/
|
||||
public MediaCodecAudioTrackRenderer(SampleSource[] sources, MediaCodecSelector mediaCodecSelector,
|
||||
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys,
|
||||
Handler eventHandler, EventListener eventListener, AudioCapabilities audioCapabilities,
|
||||
int streamType) {
|
||||
super(sources, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler,
|
||||
eventListener);
|
||||
this.eventListener = eventListener;
|
||||
this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
|
||||
this.audioTrack = new AudioTrack(audioCapabilities, streamType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean handlesTrack(MediaCodecSelector mediaCodecSelector, MediaFormat mediaFormat)
|
||||
throws DecoderQueryException {
|
||||
String mimeType = mediaFormat.mimeType;
|
||||
return MimeTypes.isAudio(mimeType) && (MimeTypes.AUDIO_UNKNOWN.equals(mimeType)
|
||||
|| (allowPassthrough(mimeType) && mediaCodecSelector.getPassthroughDecoderInfo() != null)
|
||||
|| mediaCodecSelector.getDecoderInfo(mimeType, false) != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DecoderInfo getDecoderInfo(MediaCodecSelector mediaCodecSelector, String mimeType,
|
||||
boolean requiresSecureDecoder) throws DecoderQueryException {
|
||||
if (allowPassthrough(mimeType)) {
|
||||
DecoderInfo passthroughDecoderInfo = mediaCodecSelector.getPassthroughDecoderInfo();
|
||||
if (passthroughDecoderInfo != null) {
|
||||
passthroughEnabled = true;
|
||||
return passthroughDecoderInfo;
|
||||
}
|
||||
}
|
||||
passthroughEnabled = false;
|
||||
return super.getDecoderInfo(mediaCodecSelector, mimeType, requiresSecureDecoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether encoded audio passthrough should be used for playing back the input format.
|
||||
* This implementation returns true if the {@link AudioTrack}'s audio capabilities indicate that
|
||||
* passthrough is supported.
|
||||
*
|
||||
* @param mimeType The type of input media.
|
||||
* @return True if passthrough playback should be used. False otherwise.
|
||||
*/
|
||||
protected boolean allowPassthrough(String mimeType) {
|
||||
return audioTrack.isPassthroughSupported(mimeType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureCodec(MediaCodec codec, boolean codecIsAdaptive,
|
||||
android.media.MediaFormat format, android.media.MediaCrypto crypto) {
|
||||
String mimeType = format.getString(android.media.MediaFormat.KEY_MIME);
|
||||
if (passthroughEnabled) {
|
||||
// Override the MIME type used to configure the codec if we are using a passthrough decoder.
|
||||
format.setString(android.media.MediaFormat.KEY_MIME, MimeTypes.AUDIO_RAW);
|
||||
codec.configure(format, null, crypto, 0);
|
||||
format.setString(android.media.MediaFormat.KEY_MIME, mimeType);
|
||||
passthroughMediaFormat = format;
|
||||
} else {
|
||||
codec.configure(format, null, crypto, 0);
|
||||
passthroughMediaFormat = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MediaClock getMediaClock() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInputFormatChanged(MediaFormatHolder holder) throws ExoPlaybackException {
|
||||
super.onInputFormatChanged(holder);
|
||||
// If the input format is anything other than PCM then we assume that the audio decoder will
|
||||
// output 16-bit PCM.
|
||||
pcmEncoding = MimeTypes.AUDIO_RAW.equals(holder.format.mimeType) ? holder.format.pcmEncoding
|
||||
: C.ENCODING_PCM_16BIT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onOutputFormatChanged(MediaCodec codec, android.media.MediaFormat outputFormat) {
|
||||
boolean passthrough = passthroughMediaFormat != null;
|
||||
String mimeType = passthrough
|
||||
? passthroughMediaFormat.getString(android.media.MediaFormat.KEY_MIME)
|
||||
: MimeTypes.AUDIO_RAW;
|
||||
android.media.MediaFormat format = passthrough ? passthroughMediaFormat : outputFormat;
|
||||
int channelCount = format.getInteger(android.media.MediaFormat.KEY_CHANNEL_COUNT);
|
||||
int sampleRate = format.getInteger(android.media.MediaFormat.KEY_SAMPLE_RATE);
|
||||
audioTrack.configure(mimeType, channelCount, sampleRate, pcmEncoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when the audio session id becomes known. Once the id is known it will not change
|
||||
* (and hence this method will not be invoked again) unless the renderer is disabled and then
|
||||
* subsequently re-enabled.
|
||||
* <p>
|
||||
* The default implementation is a no-op. One reason for overriding this method would be to
|
||||
* instantiate and enable a {@link Virtualizer} in order to spatialize the audio channels. For
|
||||
* this use case, any {@link Virtualizer} instances should be released in {@link #onDisabled()}
|
||||
* (if not before).
|
||||
*
|
||||
* @param audioSessionId The audio session id.
|
||||
*/
|
||||
protected void onAudioSessionId(int audioSessionId) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStarted() {
|
||||
super.onStarted();
|
||||
audioTrack.play();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStopped() {
|
||||
audioTrack.pause();
|
||||
super.onStopped();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnded() {
|
||||
return super.isEnded() && !audioTrack.hasPendingData();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isReady() {
|
||||
return audioTrack.hasPendingData() || super.isReady();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPositionUs() {
|
||||
long newCurrentPositionUs = audioTrack.getCurrentPositionUs(isEnded());
|
||||
if (newCurrentPositionUs != AudioTrack.CURRENT_POSITION_NOT_SET) {
|
||||
currentPositionUs = allowPositionDiscontinuity ? newCurrentPositionUs
|
||||
: Math.max(currentPositionUs, newCurrentPositionUs);
|
||||
allowPositionDiscontinuity = false;
|
||||
}
|
||||
return currentPositionUs;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDisabled() throws ExoPlaybackException {
|
||||
audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
|
||||
try {
|
||||
audioTrack.release();
|
||||
} finally {
|
||||
super.onDisabled();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDiscontinuity(long positionUs) throws ExoPlaybackException {
|
||||
super.onDiscontinuity(positionUs);
|
||||
audioTrack.reset();
|
||||
currentPositionUs = positionUs;
|
||||
allowPositionDiscontinuity = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean processOutputBuffer(long positionUs, long elapsedRealtimeUs, MediaCodec codec,
|
||||
ByteBuffer buffer, MediaCodec.BufferInfo bufferInfo, int bufferIndex, boolean shouldSkip)
|
||||
throws ExoPlaybackException {
|
||||
if (passthroughEnabled && (bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
|
||||
// Discard output buffers from the passthrough (raw) decoder containing codec specific data.
|
||||
codec.releaseOutputBuffer(bufferIndex, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (shouldSkip) {
|
||||
codec.releaseOutputBuffer(bufferIndex, false);
|
||||
codecCounters.skippedOutputBufferCount++;
|
||||
audioTrack.handleDiscontinuity();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!audioTrack.isInitialized()) {
|
||||
// Initialize the AudioTrack now.
|
||||
try {
|
||||
if (audioSessionId != AudioTrack.SESSION_ID_NOT_SET) {
|
||||
audioTrack.initialize(audioSessionId);
|
||||
} else {
|
||||
audioSessionId = audioTrack.initialize();
|
||||
onAudioSessionId(audioSessionId);
|
||||
}
|
||||
audioTrackHasData = false;
|
||||
} catch (AudioTrack.InitializationException e) {
|
||||
notifyAudioTrackInitializationError(e);
|
||||
throw new ExoPlaybackException(e);
|
||||
}
|
||||
if (getState() == TrackRenderer.STATE_STARTED) {
|
||||
audioTrack.play();
|
||||
}
|
||||
} else {
|
||||
// Check for AudioTrack underrun.
|
||||
boolean audioTrackHadData = audioTrackHasData;
|
||||
audioTrackHasData = audioTrack.hasPendingData();
|
||||
if (audioTrackHadData && !audioTrackHasData && getState() == TrackRenderer.STATE_STARTED) {
|
||||
long elapsedSinceLastFeedMs = SystemClock.elapsedRealtime() - lastFeedElapsedRealtimeMs;
|
||||
long bufferSizeUs = audioTrack.getBufferSizeUs();
|
||||
long bufferSizeMs = bufferSizeUs == C.UNKNOWN_TIME_US ? -1 : bufferSizeUs / 1000;
|
||||
notifyAudioTrackUnderrun(audioTrack.getBufferSize(), bufferSizeMs, elapsedSinceLastFeedMs);
|
||||
}
|
||||
}
|
||||
|
||||
int handleBufferResult;
|
||||
try {
|
||||
handleBufferResult = audioTrack.handleBuffer(
|
||||
buffer, bufferInfo.offset, bufferInfo.size, bufferInfo.presentationTimeUs);
|
||||
lastFeedElapsedRealtimeMs = SystemClock.elapsedRealtime();
|
||||
} catch (AudioTrack.WriteException e) {
|
||||
notifyAudioTrackWriteError(e);
|
||||
throw new ExoPlaybackException(e);
|
||||
}
|
||||
|
||||
// If we are out of sync, allow currentPositionUs to jump backwards.
|
||||
if ((handleBufferResult & AudioTrack.RESULT_POSITION_DISCONTINUITY) != 0) {
|
||||
handleAudioTrackDiscontinuity();
|
||||
allowPositionDiscontinuity = true;
|
||||
}
|
||||
|
||||
// Release the buffer if it was consumed.
|
||||
if ((handleBufferResult & AudioTrack.RESULT_BUFFER_CONSUMED) != 0) {
|
||||
codec.releaseOutputBuffer(bufferIndex, false);
|
||||
codecCounters.renderedOutputBufferCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onOutputStreamEnded() {
|
||||
audioTrack.handleEndOfStream();
|
||||
}
|
||||
|
||||
protected void handleAudioTrackDiscontinuity() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(int messageType, Object message) throws ExoPlaybackException {
|
||||
switch (messageType) {
|
||||
case MSG_SET_VOLUME:
|
||||
audioTrack.setVolume((Float) message);
|
||||
break;
|
||||
case MSG_SET_PLAYBACK_PARAMS:
|
||||
audioTrack.setPlaybackParams((PlaybackParams) message);
|
||||
break;
|
||||
default:
|
||||
super.handleMessage(messageType, message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyAudioTrackInitializationError(final AudioTrack.InitializationException e) {
|
||||
if (eventHandler != null && eventListener != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onAudioTrackInitializationError(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyAudioTrackWriteError(final AudioTrack.WriteException e) {
|
||||
if (eventHandler != null && eventListener != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onAudioTrackWriteError(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyAudioTrackUnderrun(final int bufferSize, final long bufferSizeMs,
|
||||
final long elapsedSinceLastFeedMs) {
|
||||
if (eventHandler != null && eventListener != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onAudioTrackUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import org.telegram.messenger.exoplayer.MediaCodecUtil.DecoderQueryException;
|
||||
|
||||
import android.media.MediaCodec;
|
||||
|
||||
/**
|
||||
* Selector of {@link MediaCodec} instances.
|
||||
*/
|
||||
public interface MediaCodecSelector {
|
||||
|
||||
/**
|
||||
* Default implementation of {@link MediaCodecSelector}.
|
||||
*/
|
||||
MediaCodecSelector DEFAULT = new MediaCodecSelector() {
|
||||
|
||||
@Override
|
||||
public DecoderInfo getDecoderInfo(String mimeType, boolean requiresSecureDecoder)
|
||||
throws DecoderQueryException {
|
||||
return MediaCodecUtil.getDecoderInfo(mimeType, requiresSecureDecoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecoderInfo getPassthroughDecoderInfo() throws DecoderQueryException {
|
||||
return MediaCodecUtil.getPassthroughDecoderInfo();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Selects a decoder to instantiate for a given mime type.
|
||||
*
|
||||
* @param mimeType The mime type for which a decoder is required.
|
||||
* @param requiresSecureDecoder Whether a secure decoder is required.
|
||||
* @return A {@link DecoderInfo} describing the decoder, or null if no suitable decoder exists.
|
||||
* @throws DecoderQueryException Thrown if there was an error querying decoders.
|
||||
*/
|
||||
DecoderInfo getDecoderInfo(String mimeType, boolean requiresSecureDecoder)
|
||||
throws DecoderQueryException;
|
||||
|
||||
/**
|
||||
* Selects a decoder to instantiate for audio passthrough.
|
||||
*
|
||||
* @return A {@link DecoderInfo} describing the decoder, or null if no suitable decoder exists.
|
||||
* @throws DecoderQueryException Thrown if there was an error querying decoders.
|
||||
*/
|
||||
DecoderInfo getPassthroughDecoderInfo() throws DecoderQueryException;
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
501
TMessagesProj/src/main/java/org/telegram/messenger/exoplayer/MediaCodecUtil.java
Executable file
501
TMessagesProj/src/main/java/org/telegram/messenger/exoplayer/MediaCodecUtil.java
Executable file
|
@ -0,0 +1,501 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import org.telegram.messenger.exoplayer.util.Assertions;
|
||||
import org.telegram.messenger.exoplayer.util.MimeTypes;
|
||||
import org.telegram.messenger.exoplayer.util.Util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.media.MediaCodecInfo;
|
||||
import android.media.MediaCodecInfo.CodecCapabilities;
|
||||
import android.media.MediaCodecInfo.CodecProfileLevel;
|
||||
import android.media.MediaCodecList;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A utility class for querying the available codecs.
|
||||
*/
|
||||
@TargetApi(16)
|
||||
public final class MediaCodecUtil {
|
||||
|
||||
/**
|
||||
* Thrown when an error occurs querying the device for its underlying media capabilities.
|
||||
* <p>
|
||||
* Such failures are not expected in normal operation and are normally temporary (e.g. if the
|
||||
* mediaserver process has crashed and is yet to restart).
|
||||
*/
|
||||
public static class DecoderQueryException extends IOException {
|
||||
|
||||
private DecoderQueryException(Throwable cause) {
|
||||
super("Failed to query underlying media codecs", cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final String TAG = "MediaCodecUtil";
|
||||
private static final DecoderInfo PASSTHROUGH_DECODER_INFO =
|
||||
new DecoderInfo("OMX.google.raw.decoder", null);
|
||||
|
||||
private static final Map<CodecKey, List<DecoderInfo>> decoderInfosCache = new HashMap<>();
|
||||
|
||||
// Lazily initialized.
|
||||
private static int maxH264DecodableFrameSize = -1;
|
||||
|
||||
private MediaCodecUtil() {}
|
||||
|
||||
/**
|
||||
* Optional call to warm the codec cache for a given mime type.
|
||||
* <p>
|
||||
* Calling this method may speed up subsequent calls to {@link #getDecoderInfo(String, boolean)}.
|
||||
*
|
||||
* @param mimeType The mime type.
|
||||
* @param secure Whether the decoder is required to support secure decryption. Always pass false
|
||||
* unless secure decryption really is required.
|
||||
*/
|
||||
public static void warmCodec(String mimeType, boolean secure) {
|
||||
try {
|
||||
getDecoderInfos(mimeType, secure);
|
||||
} catch (DecoderQueryException e) {
|
||||
// Codec warming is best effort, so we can swallow the exception.
|
||||
Log.e(TAG, "Codec warming failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information about a decoder suitable for audio passthrough.
|
||||
**
|
||||
* @return A {@link DecoderInfo} describing the decoder, or null if no suitable decoder exists.
|
||||
*/
|
||||
public static DecoderInfo getPassthroughDecoderInfo() {
|
||||
// TODO: Return null if the raw decoder doesn't exist.
|
||||
return PASSTHROUGH_DECODER_INFO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information about the preferred decoder for a given mime type.
|
||||
*
|
||||
* @param mimeType The mime type.
|
||||
* @param secure Whether the decoder is required to support secure decryption. Always pass false
|
||||
* unless secure decryption really is required.
|
||||
* @return A {@link DecoderInfo} describing the decoder, or null if no suitable decoder exists.
|
||||
*/
|
||||
public static DecoderInfo getDecoderInfo(String mimeType, boolean secure)
|
||||
throws DecoderQueryException {
|
||||
List<DecoderInfo> decoderInfos = getDecoderInfos(mimeType, secure);
|
||||
return decoderInfos.isEmpty() ? null : decoderInfos.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all @{link DecoderInfo}s for a given mime type, in the order given by
|
||||
* {@link MediaCodecList}.
|
||||
*
|
||||
* @param mimeType The mime type.
|
||||
* @param secure Whether the decoders are required to support secure decryption. Always pass false
|
||||
* unless secure decryption really is required.
|
||||
* @return A list of all @{link DecoderInfo}s for the given mime type. May be empty if no suitable
|
||||
* decoders exist.
|
||||
*/
|
||||
public static synchronized List<DecoderInfo> getDecoderInfos(String mimeType, boolean secure)
|
||||
throws DecoderQueryException {
|
||||
CodecKey key = new CodecKey(mimeType, secure);
|
||||
List<DecoderInfo> decoderInfos = decoderInfosCache.get(key);
|
||||
if (decoderInfos != null) {
|
||||
return decoderInfos;
|
||||
}
|
||||
MediaCodecListCompat mediaCodecList = Util.SDK_INT >= 21
|
||||
? new MediaCodecListCompatV21(secure) : new MediaCodecListCompatV16();
|
||||
decoderInfos = getDecoderInfosInternal(key, mediaCodecList);
|
||||
if (secure && decoderInfos.isEmpty() && 21 <= Util.SDK_INT && Util.SDK_INT <= 23) {
|
||||
// Some devices don't list secure decoders on API level 21 [Internal: b/18678462]. Try the
|
||||
// legacy path. We also try this path on API levels 22 and 23 as a defensive measure.
|
||||
mediaCodecList = new MediaCodecListCompatV16();
|
||||
decoderInfos = getDecoderInfosInternal(key, mediaCodecList);
|
||||
if (!decoderInfos.isEmpty()) {
|
||||
Log.w(TAG, "MediaCodecList API didn't list secure decoder for: " + mimeType
|
||||
+ ". Assuming: " + decoderInfos.get(0).name);
|
||||
}
|
||||
}
|
||||
decoderInfos = Collections.unmodifiableList(decoderInfos);
|
||||
decoderInfosCache.put(key, decoderInfos);
|
||||
return decoderInfos;
|
||||
}
|
||||
|
||||
private static List<DecoderInfo> getDecoderInfosInternal(
|
||||
CodecKey key, MediaCodecListCompat mediaCodecList) throws DecoderQueryException {
|
||||
try {
|
||||
List<DecoderInfo> decoderInfos = new ArrayList<>();
|
||||
String mimeType = key.mimeType;
|
||||
int numberOfCodecs = mediaCodecList.getCodecCount();
|
||||
boolean secureDecodersExplicit = mediaCodecList.secureDecodersExplicit();
|
||||
// Note: MediaCodecList is sorted by the framework such that the best decoders come first.
|
||||
for (int i = 0; i < numberOfCodecs; i++) {
|
||||
MediaCodecInfo codecInfo = mediaCodecList.getCodecInfoAt(i);
|
||||
String codecName = codecInfo.getName();
|
||||
if (isCodecUsableDecoder(codecInfo, codecName, secureDecodersExplicit)) {
|
||||
for (String supportedType : codecInfo.getSupportedTypes()) {
|
||||
if (supportedType.equalsIgnoreCase(mimeType)) {
|
||||
try {
|
||||
CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(supportedType);
|
||||
boolean secure = mediaCodecList.isSecurePlaybackSupported(mimeType, capabilities);
|
||||
if ((secureDecodersExplicit && key.secure == secure)
|
||||
|| (!secureDecodersExplicit && !key.secure)) {
|
||||
decoderInfos.add(new DecoderInfo(codecName, capabilities));
|
||||
} else if (!secureDecodersExplicit && secure) {
|
||||
decoderInfos.add(new DecoderInfo(codecName + ".secure", capabilities));
|
||||
// It only makes sense to have one synthesized secure decoder, return immediately.
|
||||
return decoderInfos;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (Util.SDK_INT <= 23 && !decoderInfos.isEmpty()) {
|
||||
// Suppress error querying secondary codec capabilities up to API level 23.
|
||||
Log.e(TAG, "Skipping codec " + codecName + " (failed to query capabilities)");
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return decoderInfos;
|
||||
} catch (Exception e) {
|
||||
// If the underlying mediaserver is in a bad state, we may catch an IllegalStateException
|
||||
// or an IllegalArgumentException here.
|
||||
throw new DecoderQueryException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified codec is usable for decoding on the current device.
|
||||
*/
|
||||
private static boolean isCodecUsableDecoder(MediaCodecInfo info, String name,
|
||||
boolean secureDecodersExplicit) {
|
||||
if (info.isEncoder() || (!secureDecodersExplicit && name.endsWith(".secure"))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Work around broken audio decoders.
|
||||
if (Util.SDK_INT < 21
|
||||
&& ("CIPAACDecoder".equals(name))
|
||||
|| "CIPMP3Decoder".equals(name)
|
||||
|| "CIPVorbisDecoder".equals(name)
|
||||
|| "AACDecoder".equals(name)
|
||||
|| "MP3Decoder".equals(name)) {
|
||||
return false;
|
||||
}
|
||||
// Work around https://github.com/google/ExoPlayer/issues/398
|
||||
if (Util.SDK_INT < 18 && "OMX.SEC.MP3.Decoder".equals(name)) {
|
||||
return false;
|
||||
}
|
||||
// Work around https://github.com/google/ExoPlayer/issues/1528
|
||||
if (Util.SDK_INT < 18 && "OMX.MTK.AUDIO.DECODER.AAC".equals(name)
|
||||
&& "a70".equals(Util.DEVICE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Work around an issue where creating a particular MP3 decoder on some devices on platform API
|
||||
// version 16 crashes mediaserver.
|
||||
if (Util.SDK_INT == 16
|
||||
&& "OMX.qcom.audio.decoder.mp3".equals(name)
|
||||
&& ("dlxu".equals(Util.DEVICE) // HTC Butterfly
|
||||
|| "protou".equals(Util.DEVICE) // HTC Desire X
|
||||
|| "C6602".equals(Util.DEVICE) // Sony Xperia Z
|
||||
|| "C6603".equals(Util.DEVICE)
|
||||
|| "C6606".equals(Util.DEVICE)
|
||||
|| "C6616".equals(Util.DEVICE)
|
||||
|| "L36h".equals(Util.DEVICE)
|
||||
|| "SO-02E".equals(Util.DEVICE))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Work around an issue where large timestamps are not propagated correctly.
|
||||
if (Util.SDK_INT == 16
|
||||
&& "OMX.qcom.audio.decoder.aac".equals(name)
|
||||
&& ("C1504".equals(Util.DEVICE) // Sony Xperia E
|
||||
|| "C1505".equals(Util.DEVICE)
|
||||
|| "C1604".equals(Util.DEVICE) // Sony Xperia E dual
|
||||
|| "C1605".equals(Util.DEVICE))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Work around an issue where the VP8 decoder on Samsung Galaxy S3/S4 Mini does not render
|
||||
// video.
|
||||
if (Util.SDK_INT <= 19 && Util.DEVICE != null
|
||||
&& (Util.DEVICE.startsWith("d2") || Util.DEVICE.startsWith("serrano"))
|
||||
&& "samsung".equals(Util.MANUFACTURER) && name.equals("OMX.SEC.vp8.dec")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the device advertises it can decode video of a given type at a specified width
|
||||
* and height.
|
||||
* <p>
|
||||
* Must not be called if the device SDK version is less than 21.
|
||||
*
|
||||
* @param mimeType The mime type.
|
||||
* @param secure Whether the decoder is required to support secure decryption. Always pass false
|
||||
* unless secure decryption really is required.
|
||||
* @param width Width in pixels.
|
||||
* @param height Height in pixels.
|
||||
* @return Whether the decoder advertises support of the given size.
|
||||
*/
|
||||
@TargetApi(21)
|
||||
public static boolean isSizeSupportedV21(String mimeType, boolean secure, int width,
|
||||
int height) throws DecoderQueryException {
|
||||
Assertions.checkState(Util.SDK_INT >= 21);
|
||||
MediaCodecInfo.VideoCapabilities videoCapabilities = getVideoCapabilitiesV21(mimeType, secure);
|
||||
return videoCapabilities != null && videoCapabilities.isSizeSupported(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the device advertises it can decode video of a given type at a specified
|
||||
* width, height, and frame rate.
|
||||
* <p>
|
||||
* Must not be called if the device SDK version is less than 21.
|
||||
*
|
||||
* @param mimeType The mime type.
|
||||
* @param secure Whether the decoder is required to support secure decryption. Always pass false
|
||||
* unless secure decryption really is required.
|
||||
* @param width Width in pixels.
|
||||
* @param height Height in pixels.
|
||||
* @param frameRate Frame rate in frames per second.
|
||||
* @return Whether the decoder advertises support of the given size and frame rate.
|
||||
*/
|
||||
@TargetApi(21)
|
||||
public static boolean isSizeAndRateSupportedV21(String mimeType, boolean secure,
|
||||
int width, int height, double frameRate) throws DecoderQueryException {
|
||||
Assertions.checkState(Util.SDK_INT >= 21);
|
||||
MediaCodecInfo.VideoCapabilities videoCapabilities = getVideoCapabilitiesV21(mimeType, secure);
|
||||
return videoCapabilities != null
|
||||
&& videoCapabilities.areSizeAndRateSupported(width, height, frameRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param profile An AVC profile constant from {@link CodecProfileLevel}.
|
||||
* @param level An AVC profile level from {@link CodecProfileLevel}.
|
||||
* @return Whether the specified profile is supported at the specified level.
|
||||
* @deprecated Prefer {@link #getDecoderInfos(String, boolean)} for new code.
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isH264ProfileSupported(int profile, int level)
|
||||
throws DecoderQueryException {
|
||||
DecoderInfo decoderInfo = getDecoderInfo(MimeTypes.VIDEO_H264, false);
|
||||
if (decoderInfo == null) {
|
||||
return false;
|
||||
}
|
||||
for (CodecProfileLevel profileLevel : decoderInfo.capabilities.profileLevels) {
|
||||
if (profileLevel.profile == profile && profileLevel.level >= level) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the maximum frame size for an H264 stream that can be decoded on the device.
|
||||
*/
|
||||
public static int maxH264DecodableFrameSize() throws DecoderQueryException {
|
||||
if (maxH264DecodableFrameSize == -1) {
|
||||
int result = 0;
|
||||
DecoderInfo decoderInfo = getDecoderInfo(MimeTypes.VIDEO_H264, false);
|
||||
if (decoderInfo != null) {
|
||||
for (CodecProfileLevel profileLevel : decoderInfo.capabilities.profileLevels) {
|
||||
result = Math.max(avcLevelToMaxFrameSize(profileLevel.level), result);
|
||||
}
|
||||
// We assume support for at least 360p.
|
||||
result = Math.max(result, 480 * 360);
|
||||
}
|
||||
maxH264DecodableFrameSize = result;
|
||||
}
|
||||
return maxH264DecodableFrameSize;
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
private static MediaCodecInfo.VideoCapabilities getVideoCapabilitiesV21(String mimeType,
|
||||
boolean secure) throws DecoderQueryException {
|
||||
DecoderInfo decoderInfo = getDecoderInfo(mimeType, secure);
|
||||
return decoderInfo == null ? null : decoderInfo.capabilities.getVideoCapabilities();
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion values taken from ISO 14496-10 Table A-1.
|
||||
*
|
||||
* @param avcLevel one of CodecProfileLevel.AVCLevel* constants.
|
||||
* @return maximum frame size that can be decoded by a decoder with the specified avc level
|
||||
* (or {@code -1} if the level is not recognized)
|
||||
*/
|
||||
private static int avcLevelToMaxFrameSize(int avcLevel) {
|
||||
switch (avcLevel) {
|
||||
case CodecProfileLevel.AVCLevel1: return 99 * 16 * 16;
|
||||
case CodecProfileLevel.AVCLevel1b: return 99 * 16 * 16;
|
||||
case CodecProfileLevel.AVCLevel12: return 396 * 16 * 16;
|
||||
case CodecProfileLevel.AVCLevel13: return 396 * 16 * 16;
|
||||
case CodecProfileLevel.AVCLevel2: return 396 * 16 * 16;
|
||||
case CodecProfileLevel.AVCLevel21: return 792 * 16 * 16;
|
||||
case CodecProfileLevel.AVCLevel22: return 1620 * 16 * 16;
|
||||
case CodecProfileLevel.AVCLevel3: return 1620 * 16 * 16;
|
||||
case CodecProfileLevel.AVCLevel31: return 3600 * 16 * 16;
|
||||
case CodecProfileLevel.AVCLevel32: return 5120 * 16 * 16;
|
||||
case CodecProfileLevel.AVCLevel4: return 8192 * 16 * 16;
|
||||
case CodecProfileLevel.AVCLevel41: return 8192 * 16 * 16;
|
||||
case CodecProfileLevel.AVCLevel42: return 8704 * 16 * 16;
|
||||
case CodecProfileLevel.AVCLevel5: return 22080 * 16 * 16;
|
||||
case CodecProfileLevel.AVCLevel51: return 36864 * 16 * 16;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private interface MediaCodecListCompat {
|
||||
|
||||
/**
|
||||
* The number of codecs in the list.
|
||||
*/
|
||||
int getCodecCount();
|
||||
|
||||
/**
|
||||
* The info at the specified index in the list.
|
||||
*
|
||||
* @param index The index.
|
||||
*/
|
||||
MediaCodecInfo getCodecInfoAt(int index);
|
||||
|
||||
/**
|
||||
* @return Returns whether secure decoders are explicitly listed, if present.
|
||||
*/
|
||||
boolean secureDecodersExplicit();
|
||||
|
||||
/**
|
||||
* Whether secure playback is supported for the given {@link CodecCapabilities}, which should
|
||||
* have been obtained from a {@link MediaCodecInfo} obtained from this list.
|
||||
*/
|
||||
boolean isSecurePlaybackSupported(String mimeType, CodecCapabilities capabilities);
|
||||
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
private static final class MediaCodecListCompatV21 implements MediaCodecListCompat {
|
||||
|
||||
private final int codecKind;
|
||||
|
||||
private MediaCodecInfo[] mediaCodecInfos;
|
||||
|
||||
public MediaCodecListCompatV21(boolean includeSecure) {
|
||||
codecKind = includeSecure ? MediaCodecList.ALL_CODECS : MediaCodecList.REGULAR_CODECS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCodecCount() {
|
||||
ensureMediaCodecInfosInitialized();
|
||||
return mediaCodecInfos.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaCodecInfo getCodecInfoAt(int index) {
|
||||
ensureMediaCodecInfosInitialized();
|
||||
return mediaCodecInfos[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean secureDecodersExplicit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecurePlaybackSupported(String mimeType, CodecCapabilities capabilities) {
|
||||
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback);
|
||||
}
|
||||
|
||||
private void ensureMediaCodecInfosInitialized() {
|
||||
if (mediaCodecInfos == null) {
|
||||
mediaCodecInfos = new MediaCodecList(codecKind).getCodecInfos();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static final class MediaCodecListCompatV16 implements MediaCodecListCompat {
|
||||
|
||||
@Override
|
||||
public int getCodecCount() {
|
||||
return MediaCodecList.getCodecCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaCodecInfo getCodecInfoAt(int index) {
|
||||
return MediaCodecList.getCodecInfoAt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean secureDecodersExplicit() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecurePlaybackSupported(String mimeType, CodecCapabilities capabilities) {
|
||||
// Secure decoders weren't explicitly listed prior to API level 21. We assume that a secure
|
||||
// H264 decoder exists.
|
||||
return MimeTypes.VIDEO_H264.equals(mimeType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class CodecKey {
|
||||
|
||||
public final String mimeType;
|
||||
public final boolean secure;
|
||||
|
||||
public CodecKey(String mimeType, boolean secure) {
|
||||
this.mimeType = mimeType;
|
||||
this.secure = secure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((mimeType == null) ? 0 : mimeType.hashCode());
|
||||
result = prime * result + (secure ? 1231 : 1237);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || obj.getClass() != CodecKey.class) {
|
||||
return false;
|
||||
}
|
||||
CodecKey other = (CodecKey) obj;
|
||||
return TextUtils.equals(mimeType, other.mimeType) && secure == other.secure;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,611 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import org.telegram.messenger.exoplayer.MediaCodecUtil.DecoderQueryException;
|
||||
import org.telegram.messenger.exoplayer.drm.DrmSessionManager;
|
||||
import org.telegram.messenger.exoplayer.util.MimeTypes;
|
||||
import org.telegram.messenger.exoplayer.util.TraceUtil;
|
||||
import org.telegram.messenger.exoplayer.util.Util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.media.MediaCodec;
|
||||
import android.media.MediaCrypto;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.view.Surface;
|
||||
import android.view.TextureView;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Decodes and renders video using {@link MediaCodec}.
|
||||
*/
|
||||
@TargetApi(16)
|
||||
public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be notified of {@link MediaCodecVideoTrackRenderer}
|
||||
* events.
|
||||
*/
|
||||
public interface EventListener extends MediaCodecTrackRenderer.EventListener {
|
||||
|
||||
/**
|
||||
* Invoked to report the number of frames dropped by the renderer. Dropped frames are reported
|
||||
* whenever the renderer is stopped having dropped frames, and optionally, whenever the count
|
||||
* reaches a specified threshold whilst the renderer is started.
|
||||
*
|
||||
* @param count The number of dropped frames.
|
||||
* @param elapsed The duration in milliseconds over which the frames were dropped. This
|
||||
* duration is timed from when the renderer was started or from when dropped frames were
|
||||
* last reported (whichever was more recent), and not from when the first of the reported
|
||||
* drops occurred.
|
||||
*/
|
||||
void onDroppedFrames(int count, long elapsed);
|
||||
|
||||
/**
|
||||
* Invoked each time there's a change in the size of the video being rendered.
|
||||
*
|
||||
* @param width The video width in pixels.
|
||||
* @param height The video height in pixels.
|
||||
* @param unappliedRotationDegrees For videos that require a rotation, this is the clockwise
|
||||
* rotation in degrees that the application should apply for the video for it to be rendered
|
||||
* in the correct orientation. This value will always be zero on API levels 21 and above,
|
||||
* since the renderer will apply all necessary rotations internally. On earlier API levels
|
||||
* this is not possible. Applications that use {@link TextureView} can apply the rotation by
|
||||
* calling {@link TextureView#setTransform}. Applications that do not expect to encounter
|
||||
* rotated videos can safely ignore this parameter.
|
||||
* @param pixelWidthHeightRatio The width to height ratio of each pixel. For the normal case
|
||||
* of square pixels this will be equal to 1.0. Different values are indicative of anamorphic
|
||||
* content.
|
||||
*/
|
||||
void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
|
||||
float pixelWidthHeightRatio);
|
||||
|
||||
/**
|
||||
* Invoked when a frame is rendered to a surface for the first time following that surface
|
||||
* having been set as the target for the renderer.
|
||||
*
|
||||
* @param surface The surface to which a first frame has been rendered.
|
||||
*/
|
||||
void onDrawnToSurface(Surface surface);
|
||||
|
||||
}
|
||||
|
||||
// TODO: Use MediaFormat constants if these get exposed through the API. See
|
||||
// [Internal: b/14127601].
|
||||
private static final String KEY_CROP_LEFT = "crop-left";
|
||||
private static final String KEY_CROP_RIGHT = "crop-right";
|
||||
private static final String KEY_CROP_BOTTOM = "crop-bottom";
|
||||
private static final String KEY_CROP_TOP = "crop-top";
|
||||
|
||||
/**
|
||||
* The type of a message that can be passed to an instance of this class via
|
||||
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object
|
||||
* should be the target {@link Surface}, or null.
|
||||
*/
|
||||
public static final int MSG_SET_SURFACE = 1;
|
||||
|
||||
private final VideoFrameReleaseTimeHelper frameReleaseTimeHelper;
|
||||
private final EventListener eventListener;
|
||||
private final long allowedJoiningTimeUs;
|
||||
private final int videoScalingMode;
|
||||
private final int maxDroppedFrameCountToNotify;
|
||||
|
||||
private Surface surface;
|
||||
private boolean reportedDrawnToSurface;
|
||||
private boolean renderedFirstFrame;
|
||||
private long joiningDeadlineUs;
|
||||
private long droppedFrameAccumulationStartTimeMs;
|
||||
private int droppedFrameCount;
|
||||
private int consecutiveDroppedFrameCount;
|
||||
|
||||
private int pendingRotationDegrees;
|
||||
private float pendingPixelWidthHeightRatio;
|
||||
private int currentWidth;
|
||||
private int currentHeight;
|
||||
private int currentUnappliedRotationDegrees;
|
||||
private float currentPixelWidthHeightRatio;
|
||||
private int lastReportedWidth;
|
||||
private int lastReportedHeight;
|
||||
private int lastReportedUnappliedRotationDegrees;
|
||||
private float lastReportedPixelWidthHeightRatio;
|
||||
|
||||
/**
|
||||
* @param context A context.
|
||||
* @param source The upstream source from which the renderer obtains samples.
|
||||
* @param mediaCodecSelector A decoder selector.
|
||||
* @param videoScalingMode The scaling mode to pass to
|
||||
* {@link MediaCodec#setVideoScalingMode(int)}.
|
||||
*/
|
||||
public MediaCodecVideoTrackRenderer(Context context, SampleSource source,
|
||||
MediaCodecSelector mediaCodecSelector, int videoScalingMode) {
|
||||
this(context, source, mediaCodecSelector, videoScalingMode, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context A context.
|
||||
* @param source The upstream source from which the renderer obtains samples.
|
||||
* @param mediaCodecSelector A decoder selector.
|
||||
* @param videoScalingMode The scaling mode to pass to
|
||||
* {@link MediaCodec#setVideoScalingMode(int)}.
|
||||
* @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer
|
||||
* can attempt to seamlessly join an ongoing playback.
|
||||
*/
|
||||
public MediaCodecVideoTrackRenderer(Context context, SampleSource source,
|
||||
MediaCodecSelector mediaCodecSelector, int videoScalingMode, long allowedJoiningTimeMs) {
|
||||
this(context, source, mediaCodecSelector, videoScalingMode, allowedJoiningTimeMs, null, null,
|
||||
-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context A context.
|
||||
* @param source The upstream source from which the renderer obtains samples.
|
||||
* @param mediaCodecSelector A decoder selector.
|
||||
* @param videoScalingMode The scaling mode to pass to
|
||||
* {@link MediaCodec#setVideoScalingMode(int)}.
|
||||
* @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer
|
||||
* can attempt to seamlessly join an ongoing playback.
|
||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||
* null if delivery of events is not required.
|
||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||
* @param maxDroppedFrameCountToNotify The maximum number of frames that can be dropped between
|
||||
* invocations of {@link EventListener#onDroppedFrames(int, long)}.
|
||||
*/
|
||||
public MediaCodecVideoTrackRenderer(Context context, SampleSource source,
|
||||
MediaCodecSelector mediaCodecSelector, int videoScalingMode, long allowedJoiningTimeMs,
|
||||
Handler eventHandler, EventListener eventListener, int maxDroppedFrameCountToNotify) {
|
||||
this(context, source, mediaCodecSelector, videoScalingMode, allowedJoiningTimeMs, null, false,
|
||||
eventHandler, eventListener, maxDroppedFrameCountToNotify);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context A context.
|
||||
* @param source The upstream source from which the renderer obtains samples.
|
||||
* @param mediaCodecSelector A decoder selector.
|
||||
* @param videoScalingMode The scaling mode to pass to
|
||||
* {@link MediaCodec#setVideoScalingMode(int)}.
|
||||
* @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer
|
||||
* can attempt to seamlessly join an ongoing playback.
|
||||
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted
|
||||
* content is not required.
|
||||
* @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions.
|
||||
* For example a media file may start with a short clear region so as to allow playback to
|
||||
* begin in parallel with key acquisision. This parameter specifies whether the renderer is
|
||||
* permitted to play clear regions of encrypted media files before {@code drmSessionManager}
|
||||
* has obtained the keys necessary to decrypt encrypted regions of the media.
|
||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||
* null if delivery of events is not required.
|
||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||
* @param maxDroppedFrameCountToNotify The maximum number of frames that can be dropped between
|
||||
* invocations of {@link EventListener#onDroppedFrames(int, long)}.
|
||||
*/
|
||||
public MediaCodecVideoTrackRenderer(Context context, SampleSource source,
|
||||
MediaCodecSelector mediaCodecSelector, int videoScalingMode, long allowedJoiningTimeMs,
|
||||
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys,
|
||||
Handler eventHandler, EventListener eventListener, int maxDroppedFrameCountToNotify) {
|
||||
super(source, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler,
|
||||
eventListener);
|
||||
this.frameReleaseTimeHelper = new VideoFrameReleaseTimeHelper(context);
|
||||
this.videoScalingMode = videoScalingMode;
|
||||
this.allowedJoiningTimeUs = allowedJoiningTimeMs * 1000;
|
||||
this.eventListener = eventListener;
|
||||
this.maxDroppedFrameCountToNotify = maxDroppedFrameCountToNotify;
|
||||
joiningDeadlineUs = -1;
|
||||
currentWidth = -1;
|
||||
currentHeight = -1;
|
||||
currentPixelWidthHeightRatio = -1;
|
||||
pendingPixelWidthHeightRatio = -1;
|
||||
lastReportedWidth = -1;
|
||||
lastReportedHeight = -1;
|
||||
lastReportedPixelWidthHeightRatio = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean handlesTrack(MediaCodecSelector mediaCodecSelector, MediaFormat mediaFormat)
|
||||
throws DecoderQueryException {
|
||||
String mimeType = mediaFormat.mimeType;
|
||||
return MimeTypes.isVideo(mimeType) && (MimeTypes.VIDEO_UNKNOWN.equals(mimeType)
|
||||
|| mediaCodecSelector.getDecoderInfo(mimeType, false) != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onEnabled(int track, long positionUs, boolean joining)
|
||||
throws ExoPlaybackException {
|
||||
super.onEnabled(track, positionUs, joining);
|
||||
if (joining && allowedJoiningTimeUs > 0) {
|
||||
joiningDeadlineUs = SystemClock.elapsedRealtime() * 1000L + allowedJoiningTimeUs;
|
||||
}
|
||||
frameReleaseTimeHelper.enable();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDiscontinuity(long positionUs) throws ExoPlaybackException {
|
||||
super.onDiscontinuity(positionUs);
|
||||
renderedFirstFrame = false;
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
joiningDeadlineUs = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isReady() {
|
||||
if (super.isReady() && (renderedFirstFrame || !codecInitialized()
|
||||
|| getSourceState() == SOURCE_STATE_READY_READ_MAY_FAIL)) {
|
||||
// Ready. If we were joining then we've now joined, so clear the joining deadline.
|
||||
joiningDeadlineUs = -1;
|
||||
return true;
|
||||
} else if (joiningDeadlineUs == -1) {
|
||||
// Not joining.
|
||||
return false;
|
||||
} else if (SystemClock.elapsedRealtime() * 1000 < joiningDeadlineUs) {
|
||||
// Joining and still within the joining deadline.
|
||||
return true;
|
||||
} else {
|
||||
// The joining deadline has been exceeded. Give up and clear the deadline.
|
||||
joiningDeadlineUs = -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStarted() {
|
||||
super.onStarted();
|
||||
droppedFrameCount = 0;
|
||||
droppedFrameAccumulationStartTimeMs = SystemClock.elapsedRealtime();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStopped() {
|
||||
joiningDeadlineUs = -1;
|
||||
maybeNotifyDroppedFrameCount();
|
||||
super.onStopped();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDisabled() throws ExoPlaybackException {
|
||||
currentWidth = -1;
|
||||
currentHeight = -1;
|
||||
currentPixelWidthHeightRatio = -1;
|
||||
pendingPixelWidthHeightRatio = -1;
|
||||
lastReportedWidth = -1;
|
||||
lastReportedHeight = -1;
|
||||
lastReportedPixelWidthHeightRatio = -1;
|
||||
frameReleaseTimeHelper.disable();
|
||||
super.onDisabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(int messageType, Object message) throws ExoPlaybackException {
|
||||
if (messageType == MSG_SET_SURFACE) {
|
||||
setSurface((Surface) message);
|
||||
} else {
|
||||
super.handleMessage(messageType, message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param surface The surface to set.
|
||||
* @throws ExoPlaybackException
|
||||
*/
|
||||
private void setSurface(Surface surface) throws ExoPlaybackException {
|
||||
if (this.surface == surface) {
|
||||
return;
|
||||
}
|
||||
this.surface = surface;
|
||||
this.reportedDrawnToSurface = false;
|
||||
int state = getState();
|
||||
if (state == TrackRenderer.STATE_ENABLED || state == TrackRenderer.STATE_STARTED) {
|
||||
releaseCodec();
|
||||
maybeInitCodec();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldInitCodec() {
|
||||
return super.shouldInitCodec() && surface != null && surface.isValid();
|
||||
}
|
||||
|
||||
// Override configureCodec to provide the surface.
|
||||
@Override
|
||||
protected void configureCodec(MediaCodec codec, boolean codecIsAdaptive,
|
||||
android.media.MediaFormat format, MediaCrypto crypto) {
|
||||
maybeSetMaxInputSize(format, codecIsAdaptive);
|
||||
codec.configure(format, surface, crypto, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInputFormatChanged(MediaFormatHolder holder) throws ExoPlaybackException {
|
||||
super.onInputFormatChanged(holder);
|
||||
pendingPixelWidthHeightRatio = holder.format.pixelWidthHeightRatio == MediaFormat.NO_VALUE ? 1
|
||||
: holder.format.pixelWidthHeightRatio;
|
||||
pendingRotationDegrees = holder.format.rotationDegrees == MediaFormat.NO_VALUE ? 0
|
||||
: holder.format.rotationDegrees;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if the first frame has been rendered (playback has not necessarily begun).
|
||||
*/
|
||||
protected final boolean haveRenderedFirstFrame() {
|
||||
return renderedFirstFrame;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onOutputFormatChanged(MediaCodec codec, android.media.MediaFormat outputFormat) {
|
||||
boolean hasCrop = outputFormat.containsKey(KEY_CROP_RIGHT)
|
||||
&& outputFormat.containsKey(KEY_CROP_LEFT) && outputFormat.containsKey(KEY_CROP_BOTTOM)
|
||||
&& outputFormat.containsKey(KEY_CROP_TOP);
|
||||
currentWidth = hasCrop
|
||||
? outputFormat.getInteger(KEY_CROP_RIGHT) - outputFormat.getInteger(KEY_CROP_LEFT) + 1
|
||||
: outputFormat.getInteger(android.media.MediaFormat.KEY_WIDTH);
|
||||
currentHeight = hasCrop
|
||||
? outputFormat.getInteger(KEY_CROP_BOTTOM) - outputFormat.getInteger(KEY_CROP_TOP) + 1
|
||||
: outputFormat.getInteger(android.media.MediaFormat.KEY_HEIGHT);
|
||||
currentPixelWidthHeightRatio = pendingPixelWidthHeightRatio;
|
||||
if (Util.SDK_INT >= 21) {
|
||||
// On API level 21 and above the decoder applies the rotation when rendering to the surface.
|
||||
// Hence currentUnappliedRotation should always be 0. For 90 and 270 degree rotations, we need
|
||||
// to flip the width, height and pixel aspect ratio to reflect the rotation that was applied.
|
||||
if (pendingRotationDegrees == 90 || pendingRotationDegrees == 270) {
|
||||
int rotatedHeight = currentWidth;
|
||||
currentWidth = currentHeight;
|
||||
currentHeight = rotatedHeight;
|
||||
currentPixelWidthHeightRatio = 1 / currentPixelWidthHeightRatio;
|
||||
}
|
||||
} else {
|
||||
// On API level 20 and below the decoder does not apply the rotation.
|
||||
currentUnappliedRotationDegrees = pendingRotationDegrees;
|
||||
}
|
||||
// Must be applied each time the output format changes.
|
||||
codec.setVideoScalingMode(videoScalingMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canReconfigureCodec(MediaCodec codec, boolean codecIsAdaptive,
|
||||
MediaFormat oldFormat, MediaFormat newFormat) {
|
||||
return newFormat.mimeType.equals(oldFormat.mimeType)
|
||||
&& (codecIsAdaptive
|
||||
|| (oldFormat.width == newFormat.width && oldFormat.height == newFormat.height));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean processOutputBuffer(long positionUs, long elapsedRealtimeUs, MediaCodec codec,
|
||||
ByteBuffer buffer, MediaCodec.BufferInfo bufferInfo, int bufferIndex, boolean shouldSkip) {
|
||||
if (shouldSkip) {
|
||||
skipOutputBuffer(codec, bufferIndex);
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!renderedFirstFrame) {
|
||||
if (Util.SDK_INT >= 21) {
|
||||
renderOutputBufferV21(codec, bufferIndex, System.nanoTime());
|
||||
} else {
|
||||
renderOutputBuffer(codec, bufferIndex);
|
||||
}
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getState() != TrackRenderer.STATE_STARTED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compute how many microseconds it is until the buffer's presentation time.
|
||||
long elapsedSinceStartOfLoopUs = (SystemClock.elapsedRealtime() * 1000) - elapsedRealtimeUs;
|
||||
long earlyUs = bufferInfo.presentationTimeUs - positionUs - elapsedSinceStartOfLoopUs;
|
||||
|
||||
// Compute the buffer's desired release time in nanoseconds.
|
||||
long systemTimeNs = System.nanoTime();
|
||||
long unadjustedFrameReleaseTimeNs = systemTimeNs + (earlyUs * 1000);
|
||||
|
||||
// Apply a timestamp adjustment, if there is one.
|
||||
long adjustedReleaseTimeNs = frameReleaseTimeHelper.adjustReleaseTime(
|
||||
bufferInfo.presentationTimeUs, unadjustedFrameReleaseTimeNs);
|
||||
earlyUs = (adjustedReleaseTimeNs - systemTimeNs) / 1000;
|
||||
|
||||
if (earlyUs < -30000) {
|
||||
// We're more than 30ms late rendering the frame.
|
||||
dropOutputBuffer(codec, bufferIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Util.SDK_INT >= 21) {
|
||||
// Let the underlying framework time the release.
|
||||
if (earlyUs < 50000) {
|
||||
renderOutputBufferV21(codec, bufferIndex, adjustedReleaseTimeNs);
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// We need to time the release ourselves.
|
||||
if (earlyUs < 30000) {
|
||||
if (earlyUs > 11000) {
|
||||
// We're a little too early to render the frame. Sleep until the frame can be rendered.
|
||||
// Note: The 11ms threshold was chosen fairly arbitrarily.
|
||||
try {
|
||||
// Subtracting 10000 rather than 11000 ensures the sleep time will be at least 1ms.
|
||||
Thread.sleep((earlyUs - 10000) / 1000);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
renderOutputBuffer(codec, bufferIndex);
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// We're either not playing, or it's not time to render the frame yet.
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void skipOutputBuffer(MediaCodec codec, int bufferIndex) {
|
||||
TraceUtil.beginSection("skipVideoBuffer");
|
||||
codec.releaseOutputBuffer(bufferIndex, false);
|
||||
TraceUtil.endSection();
|
||||
codecCounters.skippedOutputBufferCount++;
|
||||
}
|
||||
|
||||
protected void dropOutputBuffer(MediaCodec codec, int bufferIndex) {
|
||||
TraceUtil.beginSection("dropVideoBuffer");
|
||||
codec.releaseOutputBuffer(bufferIndex, false);
|
||||
TraceUtil.endSection();
|
||||
codecCounters.droppedOutputBufferCount++;
|
||||
droppedFrameCount++;
|
||||
consecutiveDroppedFrameCount++;
|
||||
codecCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(consecutiveDroppedFrameCount,
|
||||
codecCounters.maxConsecutiveDroppedOutputBufferCount);
|
||||
if (droppedFrameCount == maxDroppedFrameCountToNotify) {
|
||||
maybeNotifyDroppedFrameCount();
|
||||
}
|
||||
}
|
||||
|
||||
protected void renderOutputBuffer(MediaCodec codec, int bufferIndex) {
|
||||
maybeNotifyVideoSizeChanged();
|
||||
TraceUtil.beginSection("releaseOutputBuffer");
|
||||
codec.releaseOutputBuffer(bufferIndex, true);
|
||||
TraceUtil.endSection();
|
||||
codecCounters.renderedOutputBufferCount++;
|
||||
renderedFirstFrame = true;
|
||||
maybeNotifyDrawnToSurface();
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
protected void renderOutputBufferV21(MediaCodec codec, int bufferIndex, long releaseTimeNs) {
|
||||
maybeNotifyVideoSizeChanged();
|
||||
TraceUtil.beginSection("releaseOutputBuffer");
|
||||
codec.releaseOutputBuffer(bufferIndex, releaseTimeNs);
|
||||
TraceUtil.endSection();
|
||||
codecCounters.renderedOutputBufferCount++;
|
||||
renderedFirstFrame = true;
|
||||
maybeNotifyDrawnToSurface();
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
private void maybeSetMaxInputSize(android.media.MediaFormat format, boolean codecIsAdaptive) {
|
||||
if (format.containsKey(android.media.MediaFormat.KEY_MAX_INPUT_SIZE)) {
|
||||
// Already set. The source of the format may know better, so do nothing.
|
||||
return;
|
||||
}
|
||||
int maxHeight = format.getInteger(android.media.MediaFormat.KEY_HEIGHT);
|
||||
if (codecIsAdaptive && format.containsKey(android.media.MediaFormat.KEY_MAX_HEIGHT)) {
|
||||
maxHeight = Math.max(maxHeight, format.getInteger(android.media.MediaFormat.KEY_MAX_HEIGHT));
|
||||
}
|
||||
int maxWidth = format.getInteger(android.media.MediaFormat.KEY_WIDTH);
|
||||
if (codecIsAdaptive && format.containsKey(android.media.MediaFormat.KEY_MAX_WIDTH)) {
|
||||
maxWidth = Math.max(maxHeight, format.getInteger(android.media.MediaFormat.KEY_MAX_WIDTH));
|
||||
}
|
||||
int maxPixels;
|
||||
int minCompressionRatio;
|
||||
switch (format.getString(android.media.MediaFormat.KEY_MIME)) {
|
||||
case MimeTypes.VIDEO_H263:
|
||||
case MimeTypes.VIDEO_MP4V:
|
||||
maxPixels = maxWidth * maxHeight;
|
||||
minCompressionRatio = 2;
|
||||
break;
|
||||
case MimeTypes.VIDEO_H264:
|
||||
if ("BRAVIA 4K 2015".equals(Util.MODEL)) {
|
||||
// The Sony BRAVIA 4k TV has input buffers that are too small for the calculated 4k video
|
||||
// maximum input size, so use the default value.
|
||||
return;
|
||||
}
|
||||
// Round up width/height to an integer number of macroblocks.
|
||||
maxPixels = ((maxWidth + 15) / 16) * ((maxHeight + 15) / 16) * 16 * 16;
|
||||
minCompressionRatio = 2;
|
||||
break;
|
||||
case MimeTypes.VIDEO_VP8:
|
||||
// VPX does not specify a ratio so use the values from the platform's SoftVPX.cpp.
|
||||
maxPixels = maxWidth * maxHeight;
|
||||
minCompressionRatio = 2;
|
||||
break;
|
||||
case MimeTypes.VIDEO_H265:
|
||||
case MimeTypes.VIDEO_VP9:
|
||||
maxPixels = maxWidth * maxHeight;
|
||||
minCompressionRatio = 4;
|
||||
break;
|
||||
default:
|
||||
// Leave the default max input size.
|
||||
return;
|
||||
}
|
||||
// Estimate the maximum input size assuming three channel 4:2:0 subsampled input frames.
|
||||
int maxInputSize = (maxPixels * 3) / (2 * minCompressionRatio);
|
||||
format.setInteger(android.media.MediaFormat.KEY_MAX_INPUT_SIZE, maxInputSize);
|
||||
}
|
||||
|
||||
private void maybeNotifyVideoSizeChanged() {
|
||||
if (eventHandler == null || eventListener == null
|
||||
|| (lastReportedWidth == currentWidth && lastReportedHeight == currentHeight
|
||||
&& lastReportedUnappliedRotationDegrees == currentUnappliedRotationDegrees
|
||||
&& lastReportedPixelWidthHeightRatio == currentPixelWidthHeightRatio)) {
|
||||
return;
|
||||
}
|
||||
// Make final copies to ensure the runnable reports the correct values.
|
||||
final int currentWidth = this.currentWidth;
|
||||
final int currentHeight = this.currentHeight;
|
||||
final int currentUnappliedRotationDegrees = this.currentUnappliedRotationDegrees;
|
||||
final float currentPixelWidthHeightRatio = this.currentPixelWidthHeightRatio;
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onVideoSizeChanged(currentWidth, currentHeight,
|
||||
currentUnappliedRotationDegrees, currentPixelWidthHeightRatio);
|
||||
}
|
||||
});
|
||||
// Update the last reported values.
|
||||
lastReportedWidth = currentWidth;
|
||||
lastReportedHeight = currentHeight;
|
||||
lastReportedUnappliedRotationDegrees = currentUnappliedRotationDegrees;
|
||||
lastReportedPixelWidthHeightRatio = currentPixelWidthHeightRatio;
|
||||
}
|
||||
|
||||
private void maybeNotifyDrawnToSurface() {
|
||||
if (eventHandler == null || eventListener == null || reportedDrawnToSurface) {
|
||||
return;
|
||||
}
|
||||
// Make a final copy to ensure the runnable reports the correct surface.
|
||||
final Surface surface = this.surface;
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onDrawnToSurface(surface);
|
||||
}
|
||||
});
|
||||
// Record that we have reported that the surface has been drawn to.
|
||||
reportedDrawnToSurface = true;
|
||||
}
|
||||
|
||||
private void maybeNotifyDroppedFrameCount() {
|
||||
if (eventHandler == null || eventListener == null || droppedFrameCount == 0) {
|
||||
return;
|
||||
}
|
||||
long now = SystemClock.elapsedRealtime();
|
||||
// Make final copies to ensure the runnable reports the correct values.
|
||||
final int countToNotify = droppedFrameCount;
|
||||
final long elapsedToNotify = now - droppedFrameAccumulationStartTimeMs;
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onDroppedFrames(countToNotify, elapsedToNotify);
|
||||
}
|
||||
});
|
||||
// Reset the dropped frame tracking.
|
||||
droppedFrameCount = 0;
|
||||
droppedFrameAccumulationStartTimeMs = now;
|
||||
}
|
||||
|
||||
}
|
502
TMessagesProj/src/main/java/org/telegram/messenger/exoplayer/MediaFormat.java
Executable file
502
TMessagesProj/src/main/java/org/telegram/messenger/exoplayer/MediaFormat.java
Executable file
|
@ -0,0 +1,502 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import org.telegram.messenger.exoplayer.util.Assertions;
|
||||
import org.telegram.messenger.exoplayer.util.MimeTypes;
|
||||
import org.telegram.messenger.exoplayer.util.Util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Defines the format of an elementary media stream.
|
||||
*/
|
||||
public final class MediaFormat implements Parcelable {
|
||||
|
||||
public static final int NO_VALUE = -1;
|
||||
|
||||
/**
|
||||
* A value for {@link #subsampleOffsetUs} to indicate that subsample timestamps are relative to
|
||||
* the timestamps of their parent samples.
|
||||
*/
|
||||
public static final long OFFSET_SAMPLE_RELATIVE = Long.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* The identifier for the track represented by the format, or null if unknown or not applicable.
|
||||
*/
|
||||
public final String trackId;
|
||||
/**
|
||||
* The mime type of the format.
|
||||
*/
|
||||
public final String mimeType;
|
||||
/**
|
||||
* The average bandwidth in bits per second, or {@link #NO_VALUE} if unknown or not applicable.
|
||||
*/
|
||||
public final int bitrate;
|
||||
/**
|
||||
* The maximum size of a buffer of data (typically one sample) in the format, or {@link #NO_VALUE}
|
||||
* if unknown or not applicable.
|
||||
*/
|
||||
public final int maxInputSize;
|
||||
/**
|
||||
* The duration in microseconds, or {@link C#UNKNOWN_TIME_US} if the duration is unknown, or
|
||||
* {@link C#MATCH_LONGEST_US} if the duration should match the duration of the longest track whose
|
||||
* duration is known.
|
||||
*/
|
||||
public final long durationUs;
|
||||
/**
|
||||
* Initialization data that must be provided to the decoder. Will not be null, but may be empty
|
||||
* if initialization data is not required.
|
||||
*/
|
||||
public final List<byte[]> initializationData;
|
||||
/**
|
||||
* Whether the format represents an adaptive track, meaning that the format of the actual media
|
||||
* data may change (e.g. to adapt to network conditions).
|
||||
*/
|
||||
public final boolean adaptive;
|
||||
|
||||
// Video specific.
|
||||
|
||||
/**
|
||||
* The width of the video in pixels, or {@link #NO_VALUE} if unknown or not applicable.
|
||||
*/
|
||||
public final int width;
|
||||
|
||||
/**
|
||||
* The height of the video in pixels, or {@link #NO_VALUE} if unknown or not applicable.
|
||||
*/
|
||||
public final int height;
|
||||
/**
|
||||
* For formats that belong to an adaptive video track (either describing the track, or describing
|
||||
* a specific format within it), this is the maximum width of the video in pixels that will be
|
||||
* encountered in the stream. Set to {@link #NO_VALUE} if unknown or not applicable.
|
||||
*/
|
||||
public final int maxWidth;
|
||||
/**
|
||||
* For formats that belong to an adaptive video track (either describing the track, or describing
|
||||
* a specific format within it), this is the maximum height of the video in pixels that will be
|
||||
* encountered in the stream. Set to {@link #NO_VALUE} if unknown or not applicable.
|
||||
*/
|
||||
public final int maxHeight;
|
||||
/**
|
||||
* The clockwise rotation that should be applied to the video for it to be rendered in the correct
|
||||
* orientation, or {@link #NO_VALUE} if unknown or not applicable. Only 0, 90, 180 and 270 are
|
||||
* supported.
|
||||
*/
|
||||
public final int rotationDegrees;
|
||||
/**
|
||||
* The width to height ratio of pixels in the video, or {@link #NO_VALUE} if unknown or not
|
||||
* applicable.
|
||||
*/
|
||||
public final float pixelWidthHeightRatio;
|
||||
|
||||
// Audio specific.
|
||||
|
||||
/**
|
||||
* The number of audio channels, or {@link #NO_VALUE} if unknown or not applicable.
|
||||
*/
|
||||
public final int channelCount;
|
||||
/**
|
||||
* The audio sampling rate in Hz, or {@link #NO_VALUE} if unknown or not applicable.
|
||||
*/
|
||||
public final int sampleRate;
|
||||
/**
|
||||
* The encoding for PCM audio streams. If {@link #mimeType} is {@link MimeTypes#AUDIO_RAW} then
|
||||
* one of {@link C#ENCODING_PCM_8BIT}, {@link C#ENCODING_PCM_16BIT}, {@link C#ENCODING_PCM_24BIT}
|
||||
* and {@link C#ENCODING_PCM_32BIT}. Set to {@link #NO_VALUE} for other media types.
|
||||
*/
|
||||
public final int pcmEncoding;
|
||||
/**
|
||||
* The number of samples to trim from the start of the decoded audio stream.
|
||||
*/
|
||||
public final int encoderDelay;
|
||||
/**
|
||||
* The number of samples to trim from the end of the decoded audio stream.
|
||||
*/
|
||||
public final int encoderPadding;
|
||||
|
||||
// Text specific.
|
||||
|
||||
/**
|
||||
* The language of the track, or null if unknown or not applicable.
|
||||
*/
|
||||
public final String language;
|
||||
|
||||
/**
|
||||
* For samples that contain subsamples, this is an offset that should be added to subsample
|
||||
* timestamps. A value of {@link #OFFSET_SAMPLE_RELATIVE} indicates that subsample timestamps are
|
||||
* relative to the timestamps of their parent samples.
|
||||
*/
|
||||
public final long subsampleOffsetUs;
|
||||
|
||||
// Lazy-initialized hashcode and framework media format.
|
||||
|
||||
private int hashCode;
|
||||
private android.media.MediaFormat frameworkMediaFormat;
|
||||
|
||||
public static MediaFormat createVideoFormat(String trackId, String mimeType, int bitrate,
|
||||
int maxInputSize, long durationUs, int width, int height, List<byte[]> initializationData) {
|
||||
return createVideoFormat(trackId, mimeType, bitrate, maxInputSize, durationUs, width, height,
|
||||
initializationData, NO_VALUE, NO_VALUE);
|
||||
}
|
||||
|
||||
public static MediaFormat createVideoFormat(String trackId, String mimeType, int bitrate,
|
||||
int maxInputSize, long durationUs, int width, int height, List<byte[]> initializationData,
|
||||
int rotationDegrees, float pixelWidthHeightRatio) {
|
||||
return new MediaFormat(trackId, mimeType, bitrate, maxInputSize, durationUs, width, height,
|
||||
rotationDegrees, pixelWidthHeightRatio, NO_VALUE, NO_VALUE, null, OFFSET_SAMPLE_RELATIVE,
|
||||
initializationData, false, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE);
|
||||
}
|
||||
|
||||
public static MediaFormat createAudioFormat(String trackId, String mimeType, int bitrate,
|
||||
int maxInputSize, long durationUs, int channelCount, int sampleRate,
|
||||
List<byte[]> initializationData, String language) {
|
||||
return createAudioFormat(trackId, mimeType, bitrate, maxInputSize, durationUs, channelCount,
|
||||
sampleRate, initializationData, language, NO_VALUE);
|
||||
}
|
||||
|
||||
public static MediaFormat createAudioFormat(String trackId, String mimeType, int bitrate,
|
||||
int maxInputSize, long durationUs, int channelCount, int sampleRate,
|
||||
List<byte[]> initializationData, String language, int pcmEncoding) {
|
||||
return new MediaFormat(trackId, mimeType, bitrate, maxInputSize, durationUs, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, channelCount, sampleRate, language, OFFSET_SAMPLE_RELATIVE,
|
||||
initializationData, false, NO_VALUE, NO_VALUE, pcmEncoding, NO_VALUE, NO_VALUE);
|
||||
}
|
||||
|
||||
public static MediaFormat createTextFormat(String trackId, String mimeType, int bitrate,
|
||||
long durationUs, String language) {
|
||||
return createTextFormat(trackId, mimeType, bitrate, durationUs, language,
|
||||
OFFSET_SAMPLE_RELATIVE);
|
||||
}
|
||||
|
||||
public static MediaFormat createTextFormat(String trackId, String mimeType, int bitrate,
|
||||
long durationUs, String language, long subsampleOffsetUs) {
|
||||
return new MediaFormat(trackId, mimeType, bitrate, NO_VALUE, durationUs, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, language, subsampleOffsetUs, null, false, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE);
|
||||
}
|
||||
|
||||
public static MediaFormat createImageFormat(String trackId, String mimeType, int bitrate,
|
||||
long durationUs, List<byte[]> initializationData, String language) {
|
||||
return new MediaFormat(trackId, mimeType, bitrate, NO_VALUE, durationUs, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, language, OFFSET_SAMPLE_RELATIVE,
|
||||
initializationData, false, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE);
|
||||
}
|
||||
|
||||
public static MediaFormat createFormatForMimeType(String trackId, String mimeType, int bitrate,
|
||||
long durationUs) {
|
||||
return new MediaFormat(trackId, mimeType, bitrate, NO_VALUE, durationUs, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, OFFSET_SAMPLE_RELATIVE, null, false, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE);
|
||||
}
|
||||
|
||||
public static MediaFormat createId3Format() {
|
||||
return createFormatForMimeType(null, MimeTypes.APPLICATION_ID3, MediaFormat.NO_VALUE,
|
||||
C.UNKNOWN_TIME_US);
|
||||
}
|
||||
|
||||
/* package */ MediaFormat(Parcel in) {
|
||||
trackId = in.readString();
|
||||
mimeType = in.readString();
|
||||
bitrate = in.readInt();
|
||||
maxInputSize = in.readInt();
|
||||
durationUs = in.readLong();
|
||||
width = in.readInt();
|
||||
height = in.readInt();
|
||||
rotationDegrees = in.readInt();
|
||||
pixelWidthHeightRatio = in.readFloat();
|
||||
channelCount = in.readInt();
|
||||
sampleRate = in.readInt();
|
||||
language = in.readString();
|
||||
subsampleOffsetUs = in.readLong();
|
||||
initializationData = new ArrayList<>();
|
||||
in.readList(initializationData, null);
|
||||
adaptive = in.readInt() == 1;
|
||||
maxWidth = in.readInt();
|
||||
maxHeight = in.readInt();
|
||||
pcmEncoding = in.readInt();
|
||||
encoderDelay = in.readInt();
|
||||
encoderPadding = in.readInt();
|
||||
}
|
||||
|
||||
/* package */ MediaFormat(String trackId, String mimeType, int bitrate, int maxInputSize,
|
||||
long durationUs, int width, int height, int rotationDegrees, float pixelWidthHeightRatio,
|
||||
int channelCount, int sampleRate, String language, long subsampleOffsetUs,
|
||||
List<byte[]> initializationData, boolean adaptive, int maxWidth, int maxHeight,
|
||||
int pcmEncoding, int encoderDelay, int encoderPadding) {
|
||||
this.trackId = trackId;
|
||||
this.mimeType = Assertions.checkNotEmpty(mimeType);
|
||||
this.bitrate = bitrate;
|
||||
this.maxInputSize = maxInputSize;
|
||||
this.durationUs = durationUs;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.rotationDegrees = rotationDegrees;
|
||||
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
|
||||
this.channelCount = channelCount;
|
||||
this.sampleRate = sampleRate;
|
||||
this.language = language;
|
||||
this.subsampleOffsetUs = subsampleOffsetUs;
|
||||
this.initializationData = initializationData == null ? Collections.<byte[]>emptyList()
|
||||
: initializationData;
|
||||
this.adaptive = adaptive;
|
||||
this.maxWidth = maxWidth;
|
||||
this.maxHeight = maxHeight;
|
||||
this.pcmEncoding = pcmEncoding;
|
||||
this.encoderDelay = encoderDelay;
|
||||
this.encoderPadding = encoderPadding;
|
||||
}
|
||||
|
||||
public MediaFormat copyWithMaxInputSize(int maxInputSize) {
|
||||
return new MediaFormat(trackId, mimeType, bitrate, maxInputSize, durationUs, width, height,
|
||||
rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate, language,
|
||||
subsampleOffsetUs, initializationData, adaptive, maxWidth, maxHeight, pcmEncoding,
|
||||
encoderDelay, encoderPadding);
|
||||
}
|
||||
|
||||
public MediaFormat copyWithMaxVideoDimensions(int maxWidth, int maxHeight) {
|
||||
return new MediaFormat(trackId, mimeType, bitrate, maxInputSize, durationUs, width, height,
|
||||
rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate, language,
|
||||
subsampleOffsetUs, initializationData, adaptive, maxWidth, maxHeight, pcmEncoding,
|
||||
encoderDelay, encoderPadding);
|
||||
}
|
||||
|
||||
public MediaFormat copyWithSubsampleOffsetUs(long subsampleOffsetUs) {
|
||||
return new MediaFormat(trackId, mimeType, bitrate, maxInputSize, durationUs, width, height,
|
||||
rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate, language,
|
||||
subsampleOffsetUs, initializationData, adaptive, maxWidth, maxHeight, pcmEncoding,
|
||||
encoderDelay, encoderPadding);
|
||||
}
|
||||
|
||||
public MediaFormat copyWithDurationUs(long durationUs) {
|
||||
return new MediaFormat(trackId, mimeType, bitrate, maxInputSize, durationUs, width, height,
|
||||
rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate, language,
|
||||
subsampleOffsetUs, initializationData, adaptive, maxWidth, maxHeight, pcmEncoding,
|
||||
encoderDelay, encoderPadding);
|
||||
}
|
||||
|
||||
public MediaFormat copyWithLanguage(String language) {
|
||||
return new MediaFormat(trackId, mimeType, bitrate, maxInputSize, durationUs, width, height,
|
||||
rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate, language,
|
||||
subsampleOffsetUs, initializationData, adaptive, maxWidth, maxHeight, pcmEncoding,
|
||||
encoderDelay, encoderPadding);
|
||||
}
|
||||
|
||||
public MediaFormat copyWithFixedTrackInfo(String trackId, int bitrate, int width, int height,
|
||||
String language) {
|
||||
return new MediaFormat(trackId, mimeType, bitrate, maxInputSize, durationUs, width, height,
|
||||
rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate, language,
|
||||
subsampleOffsetUs, initializationData, adaptive, NO_VALUE, NO_VALUE, pcmEncoding,
|
||||
encoderDelay, encoderPadding);
|
||||
}
|
||||
|
||||
public MediaFormat copyAsAdaptive(String trackId) {
|
||||
return new MediaFormat(trackId, mimeType, NO_VALUE, NO_VALUE, durationUs, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, OFFSET_SAMPLE_RELATIVE, null, true, maxWidth,
|
||||
maxHeight, NO_VALUE, NO_VALUE, NO_VALUE);
|
||||
}
|
||||
|
||||
public MediaFormat copyWithGaplessInfo(int encoderDelay, int encoderPadding) {
|
||||
return new MediaFormat(trackId, mimeType, bitrate, maxInputSize, durationUs, width, height,
|
||||
rotationDegrees, pixelWidthHeightRatio, channelCount, sampleRate, language,
|
||||
subsampleOffsetUs, initializationData, adaptive, maxWidth, maxHeight, pcmEncoding,
|
||||
encoderDelay, encoderPadding);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A {@link MediaFormat} representation of this format.
|
||||
*/
|
||||
@SuppressLint("InlinedApi")
|
||||
@TargetApi(16)
|
||||
public final android.media.MediaFormat getFrameworkMediaFormatV16() {
|
||||
if (frameworkMediaFormat == null) {
|
||||
android.media.MediaFormat format = new android.media.MediaFormat();
|
||||
format.setString(android.media.MediaFormat.KEY_MIME, mimeType);
|
||||
maybeSetStringV16(format, android.media.MediaFormat.KEY_LANGUAGE, language);
|
||||
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_MAX_INPUT_SIZE, maxInputSize);
|
||||
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_WIDTH, width);
|
||||
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_HEIGHT, height);
|
||||
maybeSetIntegerV16(format, "rotation-degrees", rotationDegrees);
|
||||
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_MAX_WIDTH, maxWidth);
|
||||
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_MAX_HEIGHT, maxHeight);
|
||||
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_CHANNEL_COUNT, channelCount);
|
||||
maybeSetIntegerV16(format, android.media.MediaFormat.KEY_SAMPLE_RATE, sampleRate);
|
||||
maybeSetIntegerV16(format, "encoder-delay", encoderDelay);
|
||||
maybeSetIntegerV16(format, "encoder-padding", encoderPadding);
|
||||
for (int i = 0; i < initializationData.size(); i++) {
|
||||
format.setByteBuffer("csd-" + i, ByteBuffer.wrap(initializationData.get(i)));
|
||||
}
|
||||
if (durationUs != C.UNKNOWN_TIME_US) {
|
||||
format.setLong(android.media.MediaFormat.KEY_DURATION, durationUs);
|
||||
}
|
||||
frameworkMediaFormat = format;
|
||||
}
|
||||
return frameworkMediaFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the framework format returned by {@link #getFrameworkMediaFormatV16()}.
|
||||
*
|
||||
* @deprecated This method only exists for FrameworkSampleSource, which is itself deprecated.
|
||||
* @param format The framework format.
|
||||
*/
|
||||
@Deprecated
|
||||
@TargetApi(16)
|
||||
/* package */ final void setFrameworkFormatV16(android.media.MediaFormat format) {
|
||||
frameworkMediaFormat = format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MediaFormat(" + trackId + ", " + mimeType + ", " + bitrate + ", " + maxInputSize
|
||||
+ ", " + width + ", " + height + ", " + rotationDegrees + ", " + pixelWidthHeightRatio
|
||||
+ ", " + channelCount + ", " + sampleRate + ", " + language + ", " + durationUs + ", "
|
||||
+ adaptive + ", " + maxWidth + ", " + maxHeight + ", " + pcmEncoding + ", " + encoderDelay
|
||||
+ ", " + encoderPadding + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (hashCode == 0) {
|
||||
int result = 17;
|
||||
result = 31 * result + (trackId == null ? 0 : trackId.hashCode());
|
||||
result = 31 * result + (mimeType == null ? 0 : mimeType.hashCode());
|
||||
result = 31 * result + bitrate;
|
||||
result = 31 * result + maxInputSize;
|
||||
result = 31 * result + width;
|
||||
result = 31 * result + height;
|
||||
result = 31 * result + rotationDegrees;
|
||||
result = 31 * result + Float.floatToRawIntBits(pixelWidthHeightRatio);
|
||||
result = 31 * result + (int) durationUs;
|
||||
result = 31 * result + (adaptive ? 1231 : 1237);
|
||||
result = 31 * result + maxWidth;
|
||||
result = 31 * result + maxHeight;
|
||||
result = 31 * result + channelCount;
|
||||
result = 31 * result + sampleRate;
|
||||
result = 31 * result + pcmEncoding;
|
||||
result = 31 * result + encoderDelay;
|
||||
result = 31 * result + encoderPadding;
|
||||
result = 31 * result + (language == null ? 0 : language.hashCode());
|
||||
result = 31 * result + (int) subsampleOffsetUs;
|
||||
for (int i = 0; i < initializationData.size(); i++) {
|
||||
result = 31 * result + Arrays.hashCode(initializationData.get(i));
|
||||
}
|
||||
hashCode = result;
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
MediaFormat other = (MediaFormat) obj;
|
||||
if (adaptive != other.adaptive || bitrate != other.bitrate || maxInputSize != other.maxInputSize
|
||||
|| durationUs != other.durationUs || width != other.width || height != other.height
|
||||
|| rotationDegrees != other.rotationDegrees
|
||||
|| pixelWidthHeightRatio != other.pixelWidthHeightRatio
|
||||
|| maxWidth != other.maxWidth || maxHeight != other.maxHeight
|
||||
|| channelCount != other.channelCount || sampleRate != other.sampleRate
|
||||
|| pcmEncoding != other.pcmEncoding || encoderDelay != other.encoderDelay
|
||||
|| encoderPadding != other.encoderPadding || subsampleOffsetUs != other.subsampleOffsetUs
|
||||
|| !Util.areEqual(trackId, other.trackId) || !Util.areEqual(language, other.language)
|
||||
|| !Util.areEqual(mimeType, other.mimeType)
|
||||
|| initializationData.size() != other.initializationData.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < initializationData.size(); i++) {
|
||||
if (!Arrays.equals(initializationData.get(i), other.initializationData.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@TargetApi(16)
|
||||
private static final void maybeSetStringV16(android.media.MediaFormat format, String key,
|
||||
String value) {
|
||||
if (value != null) {
|
||||
format.setString(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(16)
|
||||
private static final void maybeSetIntegerV16(android.media.MediaFormat format, String key,
|
||||
int value) {
|
||||
if (value != NO_VALUE) {
|
||||
format.setInteger(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
// Parcelable implementation.
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(trackId);
|
||||
dest.writeString(mimeType);
|
||||
dest.writeInt(bitrate);
|
||||
dest.writeInt(maxInputSize);
|
||||
dest.writeLong(durationUs);
|
||||
dest.writeInt(width);
|
||||
dest.writeInt(height);
|
||||
dest.writeInt(rotationDegrees);
|
||||
dest.writeFloat(pixelWidthHeightRatio);
|
||||
dest.writeInt(channelCount);
|
||||
dest.writeInt(sampleRate);
|
||||
dest.writeString(language);
|
||||
dest.writeLong(subsampleOffsetUs);
|
||||
dest.writeList(initializationData);
|
||||
dest.writeInt(adaptive ? 1 : 0);
|
||||
dest.writeInt(maxWidth);
|
||||
dest.writeInt(maxHeight);
|
||||
dest.writeInt(pcmEncoding);
|
||||
dest.writeInt(encoderDelay);
|
||||
dest.writeInt(encoderPadding);
|
||||
}
|
||||
|
||||
public static final Creator<MediaFormat> CREATOR = new Creator<MediaFormat>() {
|
||||
|
||||
@Override
|
||||
public MediaFormat createFromParcel(Parcel in) {
|
||||
return new MediaFormat(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaFormat[] newArray(int size) {
|
||||
return new MediaFormat[size];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import org.telegram.messenger.exoplayer.drm.DrmInitData;
|
||||
|
||||
/**
|
||||
* Holds a {@link MediaFormat} and corresponding drm scheme initialization data.
|
||||
*/
|
||||
public final class MediaFormatHolder {
|
||||
|
||||
/**
|
||||
* The format of the media.
|
||||
*/
|
||||
public MediaFormat format;
|
||||
/**
|
||||
* Initialization data for drm schemes supported by the media. Null if the media is not encrypted.
|
||||
*/
|
||||
public DrmInitData drmInitData;
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Thrown when an error occurs parsing media data.
|
||||
*/
|
||||
public class ParserException extends IOException {
|
||||
|
||||
public ParserException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ParserException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ParserException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public ParserException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
153
TMessagesProj/src/main/java/org/telegram/messenger/exoplayer/SampleHolder.java
Executable file
153
TMessagesProj/src/main/java/org/telegram/messenger/exoplayer/SampleHolder.java
Executable file
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Holds sample data and corresponding metadata.
|
||||
*/
|
||||
public final class SampleHolder {
|
||||
|
||||
/**
|
||||
* Disallows buffer replacement.
|
||||
*/
|
||||
public static final int BUFFER_REPLACEMENT_MODE_DISABLED = 0;
|
||||
|
||||
/**
|
||||
* Allows buffer replacement using {@link ByteBuffer#allocate(int)}.
|
||||
*/
|
||||
public static final int BUFFER_REPLACEMENT_MODE_NORMAL = 1;
|
||||
|
||||
/**
|
||||
* Allows buffer replacement using {@link ByteBuffer#allocateDirect(int)}.
|
||||
*/
|
||||
public static final int BUFFER_REPLACEMENT_MODE_DIRECT = 2;
|
||||
|
||||
public final CryptoInfo cryptoInfo;
|
||||
|
||||
/**
|
||||
* A buffer holding the sample data.
|
||||
*/
|
||||
public ByteBuffer data;
|
||||
|
||||
/**
|
||||
* The size of the sample in bytes.
|
||||
*/
|
||||
public int size;
|
||||
|
||||
/**
|
||||
* Flags that accompany the sample. A combination of {@link C#SAMPLE_FLAG_SYNC},
|
||||
* {@link C#SAMPLE_FLAG_ENCRYPTED} and {@link C#SAMPLE_FLAG_DECODE_ONLY}.
|
||||
*/
|
||||
public int flags;
|
||||
|
||||
/**
|
||||
* The time at which the sample should be presented.
|
||||
*/
|
||||
public long timeUs;
|
||||
|
||||
private final int bufferReplacementMode;
|
||||
|
||||
/**
|
||||
* @param bufferReplacementMode Determines the behavior of {@link #ensureSpaceForWrite(int)}. One
|
||||
* of {@link #BUFFER_REPLACEMENT_MODE_DISABLED}, {@link #BUFFER_REPLACEMENT_MODE_NORMAL} and
|
||||
* {@link #BUFFER_REPLACEMENT_MODE_DIRECT}.
|
||||
*/
|
||||
public SampleHolder(int bufferReplacementMode) {
|
||||
this.cryptoInfo = new CryptoInfo();
|
||||
this.bufferReplacementMode = bufferReplacementMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that {@link #data} is large enough to accommodate a write of a given length at its
|
||||
* current position.
|
||||
* <p>
|
||||
* If the capacity of {@link #data} is sufficient this method does nothing. If the capacity is
|
||||
* insufficient then an attempt is made to replace {@link #data} with a new {@link ByteBuffer}
|
||||
* whose capacity is sufficient. Data up to the current position is copied to the new buffer.
|
||||
*
|
||||
* @param length The length of the write that must be accommodated, in bytes.
|
||||
* @throws IllegalStateException If there is insufficient capacity to accommodate the write and
|
||||
* the buffer replacement mode of the holder is {@link #BUFFER_REPLACEMENT_MODE_DISABLED}.
|
||||
*/
|
||||
public void ensureSpaceForWrite(int length) throws IllegalStateException {
|
||||
if (data == null) {
|
||||
data = createReplacementBuffer(length);
|
||||
return;
|
||||
}
|
||||
// Check whether the current buffer is sufficient.
|
||||
int capacity = data.capacity();
|
||||
int position = data.position();
|
||||
int requiredCapacity = position + length;
|
||||
if (capacity >= requiredCapacity) {
|
||||
return;
|
||||
}
|
||||
// Instantiate a new buffer if possible.
|
||||
ByteBuffer newData = createReplacementBuffer(requiredCapacity);
|
||||
// Copy data up to the current position from the old buffer to the new one.
|
||||
if (position > 0) {
|
||||
data.position(0);
|
||||
data.limit(position);
|
||||
newData.put(data);
|
||||
}
|
||||
// Set the new buffer.
|
||||
data = newData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether {@link #flags} has {@link C#SAMPLE_FLAG_ENCRYPTED} set.
|
||||
*/
|
||||
public boolean isEncrypted() {
|
||||
return (flags & C.SAMPLE_FLAG_ENCRYPTED) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether {@link #flags} has {@link C#SAMPLE_FLAG_DECODE_ONLY} set.
|
||||
*/
|
||||
public boolean isDecodeOnly() {
|
||||
return (flags & C.SAMPLE_FLAG_DECODE_ONLY) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether {@link #flags} has {@link C#SAMPLE_FLAG_SYNC} set.
|
||||
*/
|
||||
public boolean isSyncFrame() {
|
||||
return (flags & C.SAMPLE_FLAG_SYNC) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears {@link #data}. Does nothing if {@link #data} is null.
|
||||
*/
|
||||
public void clearData() {
|
||||
if (data != null) {
|
||||
data.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private ByteBuffer createReplacementBuffer(int requiredCapacity) {
|
||||
if (bufferReplacementMode == BUFFER_REPLACEMENT_MODE_NORMAL) {
|
||||
return ByteBuffer.allocate(requiredCapacity);
|
||||
} else if (bufferReplacementMode == BUFFER_REPLACEMENT_MODE_DIRECT) {
|
||||
return ByteBuffer.allocateDirect(requiredCapacity);
|
||||
} else {
|
||||
int currentCapacity = data == null ? 0 : data.capacity();
|
||||
throw new IllegalStateException("Buffer too small (" + currentCapacity + " < "
|
||||
+ requiredCapacity + ")");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
214
TMessagesProj/src/main/java/org/telegram/messenger/exoplayer/SampleSource.java
Executable file
214
TMessagesProj/src/main/java/org/telegram/messenger/exoplayer/SampleSource.java
Executable file
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.telegram.messenger.exoplayer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A source of media samples.
|
||||
* <p>
|
||||
* A {@link SampleSource} may expose one or multiple tracks. The number of tracks and each track's
|
||||
* media format can be queried using {@link SampleSourceReader#getTrackCount()} and
|
||||
* {@link SampleSourceReader#getFormat(int)} respectively.
|
||||
*/
|
||||
public interface SampleSource {
|
||||
|
||||
/**
|
||||
* The end of stream has been reached.
|
||||
*/
|
||||
public static final int END_OF_STREAM = -1;
|
||||
/**
|
||||
* Neither a sample nor a format was read in full. This may be because insufficient data is
|
||||
* buffered upstream. If multiple tracks are enabled, this return value may indicate that the
|
||||
* next piece of data to be returned from the {@link SampleSource} corresponds to a different
|
||||
* track than the one for which data was requested.
|
||||
*/
|
||||
public static final int NOTHING_READ = -2;
|
||||
/**
|
||||
* A sample was read.
|
||||
*/
|
||||
public static final int SAMPLE_READ = -3;
|
||||
/**
|
||||
* A format was read.
|
||||
*/
|
||||
public static final int FORMAT_READ = -4;
|
||||
/**
|
||||
* Returned from {@link SampleSourceReader#readDiscontinuity(int)} to indicate no discontinuity.
|
||||
*/
|
||||
public static final long NO_DISCONTINUITY = Long.MIN_VALUE;
|
||||
|
||||
/**
|
||||
* A consumer of samples should call this method to register themselves and gain access to the
|
||||
* source through the returned {@link SampleSourceReader}.
|
||||
* <p>
|
||||
* {@link SampleSourceReader#release()} should be called on the returned object when access is no
|
||||
* longer required.
|
||||
*
|
||||
* @return A {@link SampleSourceReader} that provides access to the source.
|
||||
*/
|
||||
public SampleSourceReader register();
|
||||
|
||||
/**
|
||||
* An interface providing read access to a {@link SampleSource}.
|
||||
*/
|
||||
public interface SampleSourceReader {
|
||||
|
||||
/**
|
||||
* If the source is currently having difficulty preparing or loading samples, then this method
|
||||
* throws the underlying error. Otherwise does nothing.
|
||||
*
|
||||
* @throws IOException The underlying error.
|
||||
*/
|
||||
public void maybeThrowError() throws IOException;
|
||||
|
||||
/**
|
||||
* Prepares the source.
|
||||
* <p>
|
||||
* Preparation may require reading from the data source (e.g. to determine the available tracks
|
||||
* and formats). If insufficient data is available then the call will return {@code false}
|
||||
* rather than block. The method can be called repeatedly until the return value indicates
|
||||
* success.
|
||||
*
|
||||
* @param positionUs The player's current playback position.
|
||||
* @return True if the source was prepared, false otherwise.
|
||||
*/
|
||||
public boolean prepare(long positionUs);
|
||||
|
||||
/**
|
||||
* Returns the number of tracks exposed by the source.
|
||||
* <p>
|
||||
* This method should only be called after the source has been prepared.
|
||||
*
|
||||
* @return The number of tracks.
|
||||
*/
|
||||
public int getTrackCount();
|
||||
|
||||
/**
|
||||
* Returns the format of the specified track.
|
||||
* <p>
|
||||
* Note that whilst the format of a track will remain constant, the format of the actual media
|
||||
* stream may change dynamically. An example of this is where the track is adaptive
|
||||
* (i.e. @link {@link MediaFormat#adaptive} is true). Hence the track formats returned through
|
||||
* this method should not be used to configure decoders. Decoder configuration should be
|
||||
* performed using the formats obtained when reading the media stream through calls to
|
||||
* {@link #readData(int, long, MediaFormatHolder, SampleHolder)}.
|
||||
* <p>
|
||||
* This method should only be called after the source has been prepared.
|
||||
*
|
||||
* @param track The track index.
|
||||
* @return The format of the specified track.
|
||||
*/
|
||||
public MediaFormat getFormat(int track);
|
||||
|
||||
/**
|
||||
* Enable the specified track. This allows the track's format and samples to be read from
|
||||
* {@link #readData(int, long, MediaFormatHolder, SampleHolder)}.
|
||||
* <p>
|
||||
* This method should only be called after the source has been prepared, and when the specified
|
||||
* track is disabled.
|
||||
*
|
||||
* @param track The track to enable.
|
||||
* @param positionUs The player's current playback position.
|
||||
*/
|
||||
public void enable(int track, long positionUs);
|
||||
|
||||
/**
|
||||
* Indicates to the source that it should still be buffering data for the specified track.
|
||||
* <p>
|
||||
* This method should only be called when the specified track is enabled.
|
||||
*
|
||||
* @param track The track to continue buffering.
|
||||
* @param positionUs The current playback position.
|
||||
* @return True if the track has available samples, or if the end of the stream has been
|
||||
* reached. False if more data needs to be buffered for samples to become available.
|
||||
*/
|
||||
public boolean continueBuffering(int track, long positionUs);
|
||||
|
||||
/**
|
||||
* Attempts to read a pending discontinuity from the source.
|
||||
* <p>
|
||||
* This method should only be called when the specified track is enabled.
|
||||
*
|
||||
* @param track The track from which to read.
|
||||
* @return If a discontinuity was read then the playback position after the discontinuity. Else
|
||||
* {@link #NO_DISCONTINUITY}.
|
||||
*/
|
||||
public long readDiscontinuity(int track);
|
||||
|
||||
/**
|
||||
* Attempts to read a sample or a new format from the source.
|
||||
* <p>
|
||||
* This method should only be called when the specified track is enabled.
|
||||
* <p>
|
||||
* Note that where multiple tracks are enabled, {@link #NOTHING_READ} may be returned if the
|
||||
* next piece of data to be read from the {@link SampleSource} corresponds to a different track
|
||||
* than the one for which data was requested.
|
||||
* <p>
|
||||
* This method will always return {@link #NOTHING_READ} in the case that there's a pending
|
||||
* discontinuity to be read from {@link #readDiscontinuity(int)} for the specified track.
|
||||
*
|
||||
* @param track The track from which to read.
|
||||
* @param positionUs The current playback position.
|
||||
* @param formatHolder A {@link MediaFormatHolder} object to populate in the case of a new
|
||||
* format.
|
||||
* @param sampleHolder A {@link SampleHolder} object to populate in the case of a new sample.
|
||||
* If the caller requires the sample data then it must ensure that {@link SampleHolder#data}
|
||||
* references a valid output buffer.
|
||||
* @return The result, which can be {@link #SAMPLE_READ}, {@link #FORMAT_READ},
|
||||
* {@link #NOTHING_READ} or {@link #END_OF_STREAM}.
|
||||
*/
|
||||
public int readData(int track, long positionUs, MediaFormatHolder formatHolder,
|
||||
SampleHolder sampleHolder);
|
||||
|
||||
/**
|
||||
* Seeks to the specified time in microseconds.
|
||||
* <p>
|
||||
* This method should only be called when at least one track is enabled.
|
||||
*
|
||||
* @param positionUs The seek position in microseconds.
|
||||
*/
|
||||
public void seekToUs(long positionUs);
|
||||
|
||||
/**
|
||||
* Returns an estimate of the position up to which data is buffered.
|
||||
* <p>
|
||||
* This method should only be called when at least one track is enabled.
|
||||
*
|
||||
* @return An estimate of the absolute position in microseconds up to which data is buffered,
|
||||
* or {@link TrackRenderer#END_OF_TRACK_US} if data is buffered to the end of the stream,
|
||||
* or {@link TrackRenderer#UNKNOWN_TIME_US} if no estimate is available.
|
||||
*/
|
||||
public long getBufferedPositionUs();
|
||||
|
||||
/**
|
||||
* Disable the specified track.
|
||||
* <p>
|
||||
* This method should only be called when the specified track is enabled.
|
||||
*
|
||||
* @param track The track to disable.
|
||||
*/
|
||||
public void disable(int track);
|
||||
|
||||
/**
|
||||
* Releases the {@link SampleSourceReader}.
|
||||
* <p>
|
||||
* This method should be called when access to the {@link SampleSource} is no longer required.
|
||||
*/
|
||||
public void release();
|
||||
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user