Merge branch 'dev'

This commit is contained in:
DrKLO 2014-12-01 09:57:52 -08:00
commit 8c50d4d03b
1168 changed files with 39604 additions and 22424 deletions

View File

@ -3,7 +3,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.13.+'
classpath 'com.android.tools.build:gradle:0.14.0'
}
}
apply plugin: 'com.android.application'
@ -17,7 +17,7 @@ tasks.withType(JavaCompile) {
}
dependencies {
compile 'com.android.support:support-v4:20.0.+'
compile 'com.android.support:support-v4:21.0.+'
compile 'com.google.android.gms:play-services:3.2.+'
compile 'net.hockeyapp.android:HockeySDK:3.0.2'
compile 'com.googlecode.mp4parser:isoparser:1.0.+'
@ -25,7 +25,7 @@ dependencies {
android {
compileSdkVersion 21
buildToolsVersion '21.0.2'
buildToolsVersion '21.1.1'
signingConfigs {
debug {
@ -43,19 +43,19 @@ android {
buildTypes {
debug {
debuggable true
jniDebugBuild false
jniDebuggable true
signingConfig signingConfigs.debug
}
release {
debuggable false
jniDebugBuild false
jniDebuggable false
signingConfig signingConfigs.release
}
foss {
debuggable false
jniDebugBuild false
jniDebuggable false
signingConfig signingConfigs.release
}
}
@ -65,22 +65,22 @@ android {
jni.srcDirs = [] //disable automatic ndk-build call
}
sourceSets {
debug {
sourceSets.debug {
manifest.srcFile 'config/debug/AndroidManifest.xml'
}
release {
sourceSets.release {
manifest.srcFile 'config/release/AndroidManifest.xml'
}
foss {
sourceSets.foss {
manifest.srcFile 'config/foss/AndroidManifest.xml'
}
}
defaultConfig {
minSdkVersion 8
targetSdkVersion 19
versionCode 374
versionName "1.9.6"
targetSdkVersion 21
versionCode 403
versionName "2.1.0"
}
}

View File

@ -23,7 +23,7 @@
android:icon="@drawable/ic_launcher"
android:label="@string/AppName"
android:theme="@style/Theme.TMessages.Start"
android:name="org.telegram.ui.ApplicationLoader"
android:name=".ApplicationLoader"
android:hardwareAccelerated="true"
android:largeHeap="true">

View File

@ -8,7 +8,7 @@
android:icon="@drawable/ic_launcher"
android:label="@string/AppName"
android:theme="@style/Theme.TMessages.Start"
android:name="org.telegram.ui.ApplicationLoader"
android:name=".ApplicationLoader"
android:hardwareAccelerated="true"
android:largeHeap="true">

View File

@ -23,7 +23,7 @@
android:icon="@drawable/ic_launcher"
android:label="@string/AppName"
android:theme="@style/Theme.TMessages.Start"
android:name="org.telegram.ui.ApplicationLoader"
android:name=".ApplicationLoader"
android:hardwareAccelerated="true"
android:largeHeap="true">

View File

@ -2,10 +2,10 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE := tmessages
LOCAL_MODULE := tmessages.4
LOCAL_CFLAGS := -w -std=gnu99 -O2 -DNULL=0 -DSOCKLEN_T=socklen_t -DLOCALE_NOT_USED -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
LOCAL_CFLAGS += -Drestrict='' -D__EMX__ -DOPUS_BUILD -DFIXED_POINT -DUSE_ALLOCA -DHAVE_LRINT -DHAVE_LRINTF -fno-math-errno
LOCAL_CFLAGS += -DANDROID_NDK -DDISABLE_IMPORTGL -fno-strict-aliasing -fprefetch-loop-arrays -DAVOID_TABLES -DANDROID_TILE_BASED_DECODE -DANDROID_ARMV6_IDCT
LOCAL_CFLAGS += -DANDROID_NDK -DDISABLE_IMPORTGL -fno-strict-aliasing -fprefetch-loop-arrays -DAVOID_TABLES -DANDROID_TILE_BASED_DECODE -DANDROID_ARMV6_IDCT -DHAVE_STRCHRNUL=0
LOCAL_CPPFLAGS := -DBSD=1 -ffast-math -O2 -funroll-loops
#LOCAL_LDLIBS := -llog
LOCAL_LDLIBS := -ljnigraphics -llog
@ -162,10 +162,23 @@ LOCAL_SRC_FILES += \
./giflib/gifalloc.c
LOCAL_SRC_FILES += \
./aes/aes_core.c \
./aes/aes_ige.c \
./aes/aes_misc.c
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_SRC_FILES += ./aes/aes_arm.S
else
ifeq ($(TARGET_ARCH_ABI),armeabi)
LOCAL_SRC_FILES += ./aes/aes_arm.S
else
ifeq ($(TARGET_ARCH_ABI),x86)
LOCAL_SRC_FILES += ./aes/aes_core.c
else
LOCAL_SRC_FILES += ./aes/aes_core.c
endif
endif
endif
LOCAL_SRC_FILES += \
./sqlite/sqlite3.c
@ -277,7 +290,6 @@ LOCAL_SRC_FILES += \
./gif.c \
./utils.c \
./image.c \
./video.c \
./fake.c
./video.c
include $(BUILD_SHARED_LIBRARY)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,51 @@
#ifndef __ARM_ARCH_H__
#define __ARM_ARCH_H__
#if !defined(__ARM_ARCH__)
# if defined(__CC_ARM)
# define __ARM_ARCH__ __TARGET_ARCH_ARM
# if defined(__BIG_ENDIAN)
# define __ARMEB__
# else
# define __ARMEL__
# endif
# elif defined(__GNUC__)
/*
* Why doesn't gcc define __ARM_ARCH__? Instead it defines
* bunch of below macros. See all_architectires[] table in
* gcc/config/arm/arm.c. On a side note it defines
* __ARMEL__/__ARMEB__ for little-/big-endian.
*/
# if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || \
defined(__ARM_ARCH_7R__)|| defined(__ARM_ARCH_7M__) || \
defined(__ARM_ARCH_7EM__)
# define __ARM_ARCH__ 7
# elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || \
defined(__ARM_ARCH_6K__)|| defined(__ARM_ARCH_6M__) || \
defined(__ARM_ARCH_6Z__)|| defined(__ARM_ARCH_6ZK__) || \
defined(__ARM_ARCH_6T2__)
# define __ARM_ARCH__ 6
# elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) || \
defined(__ARM_ARCH_5E__)|| defined(__ARM_ARCH_5TE__) || \
defined(__ARM_ARCH_5TEJ__)
# define __ARM_ARCH__ 5
# elif defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__)
# define __ARM_ARCH__ 4
# else
# error "unsupported ARM architecture"
# endif
# endif
#endif
#ifdef OPENSSL_FIPSCANISTER
#include <openssl/fipssyms.h>
#endif
#if !__ASSEMBLER__
extern unsigned int OPENSSL_armcap_P;
#define ARMV7_NEON (1<<0)
#define ARMV7_TICK (1<<1)
#endif
#endif

View File

@ -1,6 +0,0 @@
#include <stdio.h>
void fakeFunction() {
printf("some androids has buggy native loader, so i should check size of libs in java to know that native library is correct. So each changed native library should has diffrent size in different app versions. This function will increase lib size for few bytes :)");
printf("");
}

View File

@ -589,7 +589,7 @@ static jint open(GifFileType *GifFileIn, int Error, int startPos, JNIEnv *env, j
return (jint)(Error == 0 ? info : NULL);
}
JNIEXPORT jlong JNICALL Java_org_telegram_ui_Views_GifDrawable_getAllocationByteCount(JNIEnv *env, jclass class, jobject gifInfo) {
JNIEXPORT jlong JNICALL Java_org_telegram_ui_Components_GifDrawable_getAllocationByteCount(JNIEnv *env, jclass class, jobject gifInfo) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL) {
return 0;
@ -602,7 +602,7 @@ JNIEXPORT jlong JNICALL Java_org_telegram_ui_Views_GifDrawable_getAllocationByte
return sum;
}
JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_reset(JNIEnv *env, jclass class, jobject gifInfo) {
JNIEXPORT void JNICALL Java_org_telegram_ui_Components_GifDrawable_reset(JNIEnv *env, jclass class, jobject gifInfo) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL) {
return;
@ -610,7 +610,7 @@ JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_reset(JNIEnv *env,
reset(info);
}
JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_setSpeedFactor(JNIEnv *env, jclass class, jobject gifInfo, jfloat factor) {
JNIEXPORT void JNICALL Java_org_telegram_ui_Components_GifDrawable_setSpeedFactor(JNIEnv *env, jclass class, jobject gifInfo, jfloat factor) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL) {
return;
@ -618,7 +618,7 @@ JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_setSpeedFactor(JNI
info->speedFactor = factor;
}
JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_seekToTime(JNIEnv *env, jclass class, jobject gifInfo, jint desiredPos, jintArray jPixels) {
JNIEXPORT void JNICALL Java_org_telegram_ui_Components_GifDrawable_seekToTime(JNIEnv *env, jclass class, jobject gifInfo, jint desiredPos, jintArray jPixels) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL || jPixels == NULL) {
return;
@ -665,7 +665,7 @@ JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_seekToTime(JNIEnv
}
}
JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_seekToFrame(JNIEnv *env, jclass class, jobject gifInfo, jint desiredIdx, jintArray jPixels) {
JNIEXPORT void JNICALL Java_org_telegram_ui_Components_GifDrawable_seekToFrame(JNIEnv *env, jclass class, jobject gifInfo, jint desiredIdx, jintArray jPixels) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL|| jPixels==NULL) {
return;
@ -701,7 +701,7 @@ JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_seekToFrame(JNIEnv
}
}
JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_renderFrame(JNIEnv *env, jclass class, jintArray jPixels, jobject gifInfo, jintArray metaData) {
JNIEXPORT void JNICALL Java_org_telegram_ui_Components_GifDrawable_renderFrame(JNIEnv *env, jclass class, jintArray jPixels, jobject gifInfo, jintArray metaData) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL || jPixels == NULL) {
return;
@ -752,7 +752,7 @@ JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_renderFrame(JNIEnv
(*env)->ReleaseIntArrayElements(env, metaData, rawMetaData, 0);
}
JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_free(JNIEnv *env, jclass class, jobject gifInfo) {
JNIEXPORT void JNICALL Java_org_telegram_ui_Components_GifDrawable_free(JNIEnv *env, jclass class, jobject gifInfo) {
if (gifInfo == NULL) {
return;
}
@ -765,7 +765,7 @@ JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_free(JNIEnv *env,
cleanUp(info);
}
JNIEXPORT jstring JNICALL Java_org_telegram_ui_Views_GifDrawable_getComment(JNIEnv *env, jclass class, jobject gifInfo) {
JNIEXPORT jstring JNICALL Java_org_telegram_ui_Components_GifDrawable_getComment(JNIEnv *env, jclass class, jobject gifInfo) {
if (gifInfo == NULL) {
return NULL;
}
@ -773,14 +773,14 @@ JNIEXPORT jstring JNICALL Java_org_telegram_ui_Views_GifDrawable_getComment(JNIE
return (*env)->NewStringUTF(env, info->comment);
}
JNIEXPORT jint JNICALL Java_org_telegram_ui_Views_GifDrawable_getLoopCount(JNIEnv *env, jclass class, jobject gifInfo) {
JNIEXPORT jint JNICALL Java_org_telegram_ui_Components_GifDrawable_getLoopCount(JNIEnv *env, jclass class, jobject gifInfo) {
if (gifInfo == NULL) {
return 0;
}
return ((GifInfo *)gifInfo)->loopCount;
}
JNIEXPORT jint JNICALL Java_org_telegram_ui_Views_GifDrawable_getDuration(JNIEnv *env, jclass class, jobject gifInfo) {
JNIEXPORT jint JNICALL Java_org_telegram_ui_Components_GifDrawable_getDuration(JNIEnv *env, jclass class, jobject gifInfo) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL) {
return 0;
@ -793,7 +793,7 @@ JNIEXPORT jint JNICALL Java_org_telegram_ui_Views_GifDrawable_getDuration(JNIEnv
return sum;
}
JNIEXPORT jint JNICALL Java_org_telegram_ui_Views_GifDrawable_getCurrentPosition(JNIEnv *env, jclass class, jobject gifInfo) {
JNIEXPORT jint JNICALL Java_org_telegram_ui_Components_GifDrawable_getCurrentPosition(JNIEnv *env, jclass class, jobject gifInfo) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL) {
return 0;
@ -811,7 +811,7 @@ JNIEXPORT jint JNICALL Java_org_telegram_ui_Views_GifDrawable_getCurrentPosition
return (int) (sum + remainder);
}
JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_saveRemainder(JNIEnv *env, jclass class, jobject gifInfo) {
JNIEXPORT void JNICALL Java_org_telegram_ui_Components_GifDrawable_saveRemainder(JNIEnv *env, jclass class, jobject gifInfo) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL) {
return;
@ -819,7 +819,7 @@ JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_saveRemainder(JNIE
info->lastFrameReaminder = getRealTime() - info->nextStartTime;
}
JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_restoreRemainder(JNIEnv *env, jclass class, jobject gifInfo) {
JNIEXPORT void JNICALL Java_org_telegram_ui_Components_GifDrawable_restoreRemainder(JNIEnv *env, jclass class, jobject gifInfo) {
GifInfo *info = (GifInfo *)gifInfo;
if (info == NULL || info->lastFrameReaminder == ULONG_MAX) {
return;
@ -828,7 +828,7 @@ JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_restoreRemainder(J
info->lastFrameReaminder = ULONG_MAX;
}
JNIEXPORT jint JNICALL Java_org_telegram_ui_Views_GifDrawable_openFile(JNIEnv *env, jclass class, jintArray metaData, jstring jfname) {
JNIEXPORT jint JNICALL Java_org_telegram_ui_Components_GifDrawable_openFile(JNIEnv *env, jclass class, jintArray metaData, jstring jfname) {
if (jfname == NULL) {
setMetaData(0, 0, 0, D_GIF_ERR_OPEN_FAILED, env, metaData);
return (jint) NULL;

View File

@ -229,10 +229,12 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_blurBitmap(JNIEnv *env, jcl
AndroidBitmap_unlockPixels(env, bitmap);
}
JNIEXPORT void Java_org_telegram_messenger_Utilities_loadBitmap(JNIEnv *env, jclass class, jstring path, jintArray bitmap, int scale, int format, int width, int height) {
JNIEXPORT void Java_org_telegram_messenger_Utilities_loadBitmap(JNIEnv *env, jclass class, jstring path, jobject bitmap, int scale, int width, int height, int stride) {
AndroidBitmapInfo info;
int i;
if ((i = AndroidBitmap_getInfo(env, bitmap, &info)) >= 0) {
char *fileName = (*env)->GetStringUTFChars(env, path, NULL);
FILE *infile;
@ -244,8 +246,6 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_loadBitmap(JNIEnv *env, jcl
jerr.pub.error_exit = my_error_exit;
if (!setjmp(jerr.setjmp_buffer)) {
unsigned char *bitmapBuf = (*env)->GetPrimitiveArrayCritical(env, bitmap, 0);
if (bitmapBuf) {
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, infile);
@ -257,22 +257,16 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_loadBitmap(JNIEnv *env, jcl
jpeg_start_decompress(&cinfo);
int row_stride = cinfo.output_width * cinfo.output_components;
JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
int stride = width;
if (format == 0) {
stride *= 4;
} else if (format == 1) {
stride *= 2;
}
unsigned char *pixels = bitmapBuf;
unsigned char *pixels;
if ((i = AndroidBitmap_lockPixels(env, bitmap, &pixels)) >= 0) {
int rowCount = min(cinfo.output_height, height);
int colCount = min(cinfo.output_width, width);
while (cinfo.output_scanline < rowCount) {
jpeg_read_scanlines(&cinfo, buffer, 1);
if (format == 0) {
//if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
if (cinfo.out_color_space == JCS_GRAYSCALE) {
for (i = 0; i < colCount; i++) {
float alpha = buffer[0][i] / 255.0f;
@ -284,24 +278,26 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_loadBitmap(JNIEnv *env, jcl
} else {
int c = 0;
for (i = 0; i < colCount; i++) {
pixels[i * 4] = buffer[0][i * 3 + 2];
pixels[i * 4] = buffer[0][i * 3];
pixels[i * 4 + 1] = buffer[0][i * 3 + 1];
pixels[i * 4 + 2] = buffer[0][i * 3];
pixels[i * 4 + 2] = buffer[0][i * 3 + 2];
pixels[i * 4 + 3] = 255;
c += 4;
}
}
} else if (format == 1) {
//} else if (info.format == ANDROID_BITMAP_FORMAT_RGB_565) {
}
//}
pixels += stride;
}
(*env)->ReleasePrimitiveArrayCritical(env, bitmap, bitmapBuf, 0);
jpeg_finish_decompress(&cinfo);
AndroidBitmap_unlockPixels(env, bitmap);
} else {
throwException(env, "can't get bitmap buff");
throwException(env, "AndroidBitmap_lockPixels() failed ! error=%d", i);
}
jpeg_finish_decompress(&cinfo);
} else {
throwException(env, "the JPEG code has signaled an error");
}
@ -313,4 +309,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_loadBitmap(JNIEnv *env, jcl
}
(*env)->ReleaseStringUTFChars(env, path, fileName);
} else {
throwException(env, "AndroidBitmap_getInfo() failed ! error=%d", i);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -107,9 +107,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION "3.8.6"
#define SQLITE_VERSION_NUMBER 3008006
#define SQLITE_SOURCE_ID "2014-08-15 11:46:33 9491ba7d738528f168657adb43a198238abde19e"
#define SQLITE_VERSION "3.8.7.1"
#define SQLITE_VERSION_NUMBER 3008007
#define SQLITE_SOURCE_ID "2014-10-29 13:59:56 3b7b72c4685aa5cf5e675c2c47ebec10d9704221"
/*
** CAPI3REF: Run-Time Library Version Numbers
@ -497,6 +497,7 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8))
/*
** CAPI3REF: Flags For File Open Operations
@ -2099,7 +2100,7 @@ SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);
** turns off all busy handlers.
**
** ^(There can only be a single busy handler for a particular
** [database connection] any any given moment. If another busy handler
** [database connection] at any given moment. If another busy handler
** was defined (using [sqlite3_busy_handler()]) prior to calling
** this routine, that other busy handler is cleared.)^
**
@ -2303,6 +2304,10 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
** sqlite3_malloc() is zero or negative then sqlite3_malloc() returns
** a NULL pointer.
**
** ^The sqlite3_malloc64(N) routine works just like
** sqlite3_malloc(N) except that N is an unsigned 64-bit integer instead
** of a signed 32-bit integer.
**
** ^Calling sqlite3_free() with a pointer previously returned
** by sqlite3_malloc() or sqlite3_realloc() releases that memory so
** that it might be reused. ^The sqlite3_free() routine is
@ -2314,24 +2319,38 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
** might result if sqlite3_free() is called with a non-NULL pointer that
** was not obtained from sqlite3_malloc() or sqlite3_realloc().
**
** ^(The sqlite3_realloc() interface attempts to resize a
** prior memory allocation to be at least N bytes, where N is the
** second parameter. The memory allocation to be resized is the first
** parameter.)^ ^ If the first parameter to sqlite3_realloc()
** ^The sqlite3_realloc(X,N) interface attempts to resize a
** prior memory allocation X to be at least N bytes.
** ^If the X parameter to sqlite3_realloc(X,N)
** is a NULL pointer then its behavior is identical to calling
** sqlite3_malloc(N) where N is the second parameter to sqlite3_realloc().
** ^If the second parameter to sqlite3_realloc() is zero or
** sqlite3_malloc(N).
** ^If the N parameter to sqlite3_realloc(X,N) is zero or
** negative then the behavior is exactly the same as calling
** sqlite3_free(P) where P is the first parameter to sqlite3_realloc().
** ^sqlite3_realloc() returns a pointer to a memory allocation
** of at least N bytes in size or NULL if sufficient memory is unavailable.
** sqlite3_free(X).
** ^sqlite3_realloc(X,N) returns a pointer to a memory allocation
** of at least N bytes in size or NULL if insufficient memory is available.
** ^If M is the size of the prior allocation, then min(N,M) bytes
** of the prior allocation are copied into the beginning of buffer returned
** by sqlite3_realloc() and the prior allocation is freed.
** ^If sqlite3_realloc() returns NULL, then the prior allocation
** is not freed.
** by sqlite3_realloc(X,N) and the prior allocation is freed.
** ^If sqlite3_realloc(X,N) returns NULL and N is positive, then the
** prior allocation is not freed.
**
** ^The memory returned by sqlite3_malloc() and sqlite3_realloc()
** ^The sqlite3_realloc64(X,N) interfaces works the same as
** sqlite3_realloc(X,N) except that N is a 64-bit unsigned integer instead
** of a 32-bit signed integer.
**
** ^If X is a memory allocation previously obtained from sqlite3_malloc(),
** sqlite3_malloc64(), sqlite3_realloc(), or sqlite3_realloc64(), then
** sqlite3_msize(X) returns the size of that memory allocation in bytes.
** ^The value returned by sqlite3_msize(X) might be larger than the number
** of bytes requested when X was allocated. ^If X is a NULL pointer then
** sqlite3_msize(X) returns zero. If X points to something that is not
** the beginning of memory allocation, or if it points to a formerly
** valid memory allocation that has now been freed, then the behavior
** of sqlite3_msize(X) is undefined and possibly harmful.
**
** ^The memory returned by sqlite3_malloc(), sqlite3_realloc(),
** sqlite3_malloc64(), and sqlite3_realloc64()
** is always aligned to at least an 8 byte boundary, or to a
** 4 byte boundary if the [SQLITE_4_BYTE_ALIGNED_MALLOC] compile-time
** option is used.
@ -2359,8 +2378,11 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
** [sqlite3_free()] or [sqlite3_realloc()].
*/
SQLITE_API void *sqlite3_malloc(int);
SQLITE_API void *sqlite3_malloc64(sqlite3_uint64);
SQLITE_API void *sqlite3_realloc(void*, int);
SQLITE_API void *sqlite3_realloc64(void*, sqlite3_uint64);
SQLITE_API void sqlite3_free(void*);
SQLITE_API sqlite3_uint64 sqlite3_msize(void*);
/*
** CAPI3REF: Memory Allocator Statistics
@ -2647,9 +2669,9 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** an English language description of the error following a failure of any
** of the sqlite3_open() routines.
**
** ^The default encoding for the database will be UTF-8 if
** sqlite3_open() or sqlite3_open_v2() is called and
** UTF-16 in the native byte order if sqlite3_open16() is used.
** ^The default encoding will be UTF-8 for databases created using
** sqlite3_open() or sqlite3_open_v2(). ^The default encoding for databases
** created using sqlite3_open16() will be UTF-16 in the native byte order.
**
** Whether or not an error occurs when it is opened, resources
** associated with the [database connection] handle should be released by
@ -2737,13 +2759,14 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** then it is interpreted as an absolute path. ^If the path does not begin
** with a '/' (meaning that the authority section is omitted from the URI)
** then the path is interpreted as a relative path.
** ^On windows, the first component of an absolute path
** is a drive specification (e.g. "C:").
** ^(On windows, the first component of an absolute path
** is a drive specification (e.g. "C:").)^
**
** [[core URI query parameters]]
** The query component of a URI may contain parameters that are interpreted
** either by SQLite itself, or by a [VFS | custom VFS implementation].
** SQLite interprets the following three query parameters:
** SQLite and its built-in [VFSes] interpret the
** following query parameters:
**
** <ul>
** <li> <b>vfs</b>: ^The "vfs" parameter may be used to specify the name of
@ -2778,11 +2801,9 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** a URI filename, its value overrides any behavior requested by setting
** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag.
**
** <li> <b>psow</b>: ^The psow parameter may be "true" (or "on" or "yes" or
** "1") or "false" (or "off" or "no" or "0") to indicate that the
** <li> <b>psow</b>: ^The psow parameter indicates whether or not the
** [powersafe overwrite] property does or does not apply to the
** storage media on which the database file resides. ^The psow query
** parameter only works for the built-in unix and Windows VFSes.
** storage media on which the database file resides.
**
** <li> <b>nolock</b>: ^The nolock parameter is a boolean query parameter
** which if set disables file locking in rollback journal modes. This
@ -3078,6 +3099,10 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
**
** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt>
** <dd>The maximum depth of recursion for triggers.</dd>)^
**
** [[SQLITE_LIMIT_WORKER_THREADS]] ^(<dt>SQLITE_LIMIT_WORKER_THREADS</dt>
** <dd>The maximum number of auxiliary worker threads that a single
** [prepared statement] may start.</dd>)^
** </dl>
*/
#define SQLITE_LIMIT_LENGTH 0
@ -3091,6 +3116,7 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
#define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8
#define SQLITE_LIMIT_VARIABLE_NUMBER 9
#define SQLITE_LIMIT_TRIGGER_DEPTH 10
#define SQLITE_LIMIT_WORKER_THREADS 11
/*
** CAPI3REF: Compiling An SQL Statement
@ -3364,18 +3390,18 @@ typedef struct sqlite3_context sqlite3_context;
** If the fourth parameter to sqlite3_bind_blob() is negative, then
** the behavior is undefined.
** If a non-negative fourth parameter is provided to sqlite3_bind_text()
** or sqlite3_bind_text16() then that parameter must be the byte offset
** or sqlite3_bind_text16() or sqlite3_bind_text64() then
** that parameter must be the byte offset
** where the NUL terminator would occur assuming the string were NUL
** terminated. If any NUL characters occur at byte offsets less than
** the value of the fourth parameter then the resulting string value will
** contain embedded NULs. The result of expressions involving strings
** with embedded NULs is undefined.
**
** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and
** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or
** ^The fifth argument to the BLOB and string binding interfaces
** is a destructor used to dispose of the BLOB or
** string after SQLite has finished with it. ^The destructor is called
** to dispose of the BLOB or string even if the call to sqlite3_bind_blob(),
** sqlite3_bind_text(), or sqlite3_bind_text16() fails.
** to dispose of the BLOB or string even if the call to bind API fails.
** ^If the fifth argument is
** the special value [SQLITE_STATIC], then SQLite assumes that the
** information is in static, unmanaged space and does not need to be freed.
@ -3383,6 +3409,14 @@ typedef struct sqlite3_context sqlite3_context;
** SQLite makes its own private copy of the data immediately, before
** the sqlite3_bind_*() routine returns.
**
** ^The sixth argument to sqlite3_bind_text64() must be one of
** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]
** to specify the encoding of the text in the third parameter. If
** the sixth argument to sqlite3_bind_text64() is not one of the
** allowed values shown above, or if the text encoding is different
** from the encoding specified by the sixth parameter, then the behavior
** is undefined.
**
** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that
** is filled with zeroes. ^A zeroblob uses a fixed amount of memory
** (just an integer to hold its size) while it is being processed.
@ -3403,6 +3437,9 @@ typedef struct sqlite3_context sqlite3_context;
**
** ^The sqlite3_bind_* routines return [SQLITE_OK] on success or an
** [error code] if anything goes wrong.
** ^[SQLITE_TOOBIG] might be returned if the size of a string or BLOB
** exceeds limits imposed by [sqlite3_limit]([SQLITE_LIMIT_LENGTH]) or
** [SQLITE_MAX_LENGTH].
** ^[SQLITE_RANGE] is returned if the parameter
** index is out of range. ^[SQLITE_NOMEM] is returned if malloc() fails.
**
@ -3410,12 +3447,16 @@ typedef struct sqlite3_context sqlite3_context;
** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()].
*/
SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
SQLITE_API int sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64,
void(*)(void*));
SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double);
SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int);
SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int);
SQLITE_API int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));
SQLITE_API int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*));
SQLITE_API int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));
SQLITE_API int sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64,
void(*)(void*), unsigned char encoding);
SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
@ -4411,6 +4452,10 @@ typedef void (*sqlite3_destructor_type)(void*);
** set the return value of the application-defined function to be
** a text string which is represented as UTF-8, UTF-16 native byte order,
** UTF-16 little endian, or UTF-16 big endian, respectively.
** ^The sqlite3_result_text64() interface sets the return value of an
** application-defined function to be a text string in an encoding
** specified by the fifth (and last) parameter, which must be one
** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE].
** ^SQLite takes the text result from the application from
** the 2nd parameter of the sqlite3_result_text* interfaces.
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
@ -4454,6 +4499,7 @@ typedef void (*sqlite3_destructor_type)(void*);
** the [sqlite3_context] pointer, the results are undefined.
*/
SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*));
SQLITE_API void sqlite3_result_blob64(sqlite3_context*,const void*,sqlite3_uint64,void(*)(void*));
SQLITE_API void sqlite3_result_double(sqlite3_context*, double);
SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int);
SQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int);
@ -4464,6 +4510,8 @@ SQLITE_API void sqlite3_result_int(sqlite3_context*, int);
SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64);
SQLITE_API void sqlite3_result_null(sqlite3_context*);
SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*));
SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64,
void(*)(void*), unsigned char encoding);
SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*));
SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*));
SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*));
@ -6160,12 +6208,13 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_ISKEYWORD 16
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
#define SQLITE_TESTCTRL_EXPLAIN_STMT 19
#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */
#define SQLITE_TESTCTRL_NEVER_CORRUPT 20
#define SQLITE_TESTCTRL_VDBE_COVERAGE 21
#define SQLITE_TESTCTRL_BYTEORDER 22
#define SQLITE_TESTCTRL_ISINIT 23
#define SQLITE_TESTCTRL_LAST 23
#define SQLITE_TESTCTRL_SORTER_MMAP 24
#define SQLITE_TESTCTRL_LAST 24
/*
** CAPI3REF: SQLite Runtime Status
@ -6356,12 +6405,12 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
** the current value is always zero.)^
**
** [[SQLITE_DBSTATUS_CACHE_USED]] ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt>
** <dd>This parameter returns the approximate number of of bytes of heap
** <dd>This parameter returns the approximate number of bytes of heap
** memory used by all pager caches associated with the database connection.)^
** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0.
**
** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt>
** <dd>This parameter returns the approximate number of of bytes of heap
** <dd>This parameter returns the approximate number of bytes of heap
** memory used to store the schema for all databases associated
** with the connection - main, temp, and any [ATTACH]-ed databases.)^
** ^The full amount of memory used by the schemas is reported, even if the
@ -6370,7 +6419,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0.
**
** [[SQLITE_DBSTATUS_STMT_USED]] ^(<dt>SQLITE_DBSTATUS_STMT_USED</dt>
** <dd>This parameter returns the approximate number of of bytes of heap
** <dd>This parameter returns the approximate number of bytes of heap
** and lookaside memory used by all prepared statements associated with
** the database connection.)^
** ^The highwater mark associated with SQLITE_DBSTATUS_STMT_USED is always 0.

View File

@ -4,10 +4,10 @@
android:installLocation="auto">
<supports-screens android:anyDensity="true"
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:normalScreens="true"
android:resizeable="true"
android:smallScreens="true"
android:xlargeScreens="true"/>
<uses-feature android:glEsVersion="0x00020000" android:required="false"/>
@ -41,20 +41,20 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:name=".ApplicationLoader"
android:allowBackup="false"
android:hardwareAccelerated="true"
android:icon="@drawable/ic_launcher"
android:label="@string/AppName"
android:theme="@style/Theme.TMessages.Start"
android:name="org.telegram.ui.ApplicationLoader"
android:hardwareAccelerated="true"
android:largeHeap="true">
android:largeHeap="true"
android:theme="@style/Theme.TMessages.Start">
<activity
android:name="org.telegram.ui.LaunchActivity"
android:windowSoftInputMode="adjustResize"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:hardwareAccelerated="true"
android:launchMode="singleTask"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@ -95,6 +95,19 @@
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="vnd.android.cursor.item/vnd.org.telegram.messenger.android.profile"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="telegram.me" android:scheme="http" />
<data android:host="telegram.me" android:scheme="https" />
</intent-filter>
<intent-filter android:icon="@drawable/ic_launcher" android:priority="1">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="tg" />
</intent-filter>
</activity>
<activity
android:name="org.telegram.ui.IntroActivity"
@ -105,9 +118,9 @@
android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize|stateHidden"
android:taskAffinity=""
android:theme="@style/Theme.TMessages.PopupNotification">
android:theme="@style/Theme.TMessages.PopupNotification"
android:windowSoftInputMode="adjustResize|stateHidden">
</activity>
<receiver android:name="org.telegram.android.SmsListener">
@ -116,8 +129,7 @@
</intent-filter>
</receiver>
<service android:name="org.telegram.android.AuthenticatorService"
android:exported="true">
<service android:name="org.telegram.android.AuthenticatorService" android:exported="true">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
@ -125,8 +137,7 @@
android:resource="@xml/auth"/>
</service>
<service android:name="org.telegram.android.ContactsSyncAdapterService"
android:exported="true">
<service android:name="org.telegram.android.ContactsSyncAdapterService" android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>

View File

@ -24,7 +24,7 @@
package org.telegram.PhoneFormat;
import org.telegram.ui.ApplicationLoader;
import org.telegram.messenger.ApplicationLoader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;

View File

@ -9,7 +9,7 @@
package org.telegram.SQLite;
import org.telegram.messenger.FileLog;
import org.telegram.ui.ApplicationLoader;
import org.telegram.messenger.ApplicationLoader;
public class SQLiteDatabase {
private final int sqliteHandle;

View File

@ -15,28 +15,38 @@ import android.content.DialogInterface;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Environment;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.util.StateSet;
import android.view.Display;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView;
import android.widget.EdgeEffect;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.R;
import org.telegram.messenger.TLRPC;
import org.telegram.messenger.UserConfig;
import org.telegram.ui.ApplicationLoader;
import org.telegram.ui.Views.NumberPicker;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.ui.Components.NumberPicker;
import org.telegram.ui.Components.TypefaceSpan;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Locale;
public class AndroidUtilities {
@ -51,37 +61,6 @@ public class AndroidUtilities {
public static Integer photoSize = null;
private static Boolean isTablet = null;
public static int[] arrColors = {0xffee4928, 0xff41a903, 0xffe09602, 0xff0f94ed, 0xff8f3bf7, 0xfffc4380, 0xff00a1c4, 0xffeb7002};
public static int[] arrUsersAvatars = {
R.drawable.user_red,
R.drawable.user_green,
R.drawable.user_yellow,
R.drawable.user_blue,
R.drawable.user_violet,
R.drawable.user_pink,
R.drawable.user_aqua,
R.drawable.user_orange};
public static int[] arrGroupsAvatars = {
R.drawable.group_red,
R.drawable.group_green,
R.drawable.group_yellow,
R.drawable.group_blue,
R.drawable.group_violet,
R.drawable.group_pink,
R.drawable.group_aqua,
R.drawable.group_orange};
public static int[] arrBroadcastAvatars = {
R.drawable.broadcast_red,
R.drawable.broadcast_green,
R.drawable.broadcast_yellow,
R.drawable.broadcast_blue,
R.drawable.broadcast_violet,
R.drawable.broadcast_pink,
R.drawable.broadcast_aqua,
R.drawable.broadcast_orange};
static {
density = ApplicationLoader.applicationContext.getResources().getDisplayMetrics().density;
checkDisplaySize();
@ -97,24 +76,22 @@ public class AndroidUtilities {
if (manager != null && manager.getDefaultDisplay() != null) {
int rotation = manager.getDefaultDisplay().getRotation();
int orientation = activity.getResources().getConfiguration().orientation;
int SCREEN_ORIENTATION_REVERSE_LANDSCAPE = 8;
int SCREEN_ORIENTATION_REVERSE_PORTRAIT = 9;
if (Build.VERSION.SDK_INT < 9) {
SCREEN_ORIENTATION_REVERSE_LANDSCAPE = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
SCREEN_ORIENTATION_REVERSE_PORTRAIT = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
}
if (rotation == Surface.ROTATION_270) {
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else {
if (Build.VERSION.SDK_INT >= 9) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
} else {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
activity.setRequestedOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
}
} else if (rotation == Surface.ROTATION_90) {
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
if (Build.VERSION.SDK_INT >= 9) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
} else {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
activity.setRequestedOrientation(SCREEN_ORIENTATION_REVERSE_PORTRAIT);
} else {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
@ -126,17 +103,9 @@ public class AndroidUtilities {
}
} else {
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
if (Build.VERSION.SDK_INT >= 9) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
activity.setRequestedOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
} else {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
} else {
if (Build.VERSION.SDK_INT >= 9) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
} else {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
activity.setRequestedOrientation(SCREEN_ORIENTATION_REVERSE_PORTRAIT);
}
}
}
@ -239,11 +208,7 @@ public class AndroidUtilities {
return new File("");
}
public static int dp(int value) {
return (int)(Math.max(1, density * value));
}
public static int dpf(float value) {
public static int dp(float value) {
return (int)Math.ceil(density * value);
}
@ -290,11 +255,11 @@ public class AndroidUtilities {
return layer & 0x0000ffff | (version << 16);
}
public static void RunOnUIThread(Runnable runnable) {
RunOnUIThread(runnable, 0);
public static void runOnUIThread(Runnable runnable) {
runOnUIThread(runnable, 0);
}
public static void RunOnUIThread(Runnable runnable, long delay) {
public static void runOnUIThread(Runnable runnable, long delay) {
if (delay == 0) {
ApplicationLoader.applicationHandler.post(runnable);
} else {
@ -302,7 +267,7 @@ public class AndroidUtilities {
}
}
public static void CancelRunOnUIThread(Runnable runnable) {
public static void cancelRunOnUIThread(Runnable runnable) {
ApplicationLoader.applicationHandler.removeCallbacks(runnable);
}
@ -337,58 +302,6 @@ public class AndroidUtilities {
}
}
public static int getColorIndex(int id) {
int[] arr;
if (id >= 0) {
arr = arrUsersAvatars;
} else {
arr = arrGroupsAvatars;
}
try {
String str;
if (id >= 0) {
str = String.format(Locale.US, "%d%d", id, UserConfig.getClientUserId());
} else {
str = String.format(Locale.US, "%d", id);
}
if (str.length() > 15) {
str = str.substring(0, 15);
}
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
byte[] digest = md.digest(str.getBytes());
int b = digest[Math.abs(id % 16)];
if (b < 0) {
b += 256;
}
return Math.abs(b) % arr.length;
} catch (Exception e) {
FileLog.e("tmessages", e);
}
return id % arr.length;
}
public static int getColorForId(int id) {
if (id / 1000 == 333) {
return 0xff0f94ed;
}
return arrColors[getColorIndex(id)];
}
public static int getUserAvatarForId(int id) {
if (id / 1000 == 333 || id / 1000 == 777) {
return R.drawable.telegram_avatar;
}
return arrUsersAvatars[getColorIndex(id)];
}
public static int getGroupAvatarForId(int id) {
return arrGroupsAvatars[getColorIndex(-Math.abs(id))];
}
public static int getBroadcastAvatarForId(int id) {
return arrBroadcastAvatars[getColorIndex(-Math.abs(id))];
}
public static int getPhotoSize() {
if (photoSize == null) {
if (Build.VERSION.SDK_INT >= 16) {
@ -438,7 +351,7 @@ public class AndroidUtilities {
} else if (encryptedChat.ttl == 60 * 60 * 24 * 7) {
numberPicker.setValue(20);
} else if (encryptedChat.ttl == 0) {
numberPicker.setValue(5);
numberPicker.setValue(0);
}
numberPicker.setFormatter(new NumberPicker.Formatter() {
@Override
@ -481,7 +394,7 @@ public class AndroidUtilities {
encryptedChat.ttl = 60 * 60 * 24 * 7;
}
if (oldValue != encryptedChat.ttl) {
SendMessagesHelper.getInstance().sendTTLMessage(encryptedChat, null);
SecretChatHelper.getInstance().sendTTLMessage(encryptedChat, null);
MessagesStorage.getInstance().updateEncryptedChatTTL(encryptedChat);
}
}
@ -501,4 +414,114 @@ public class AndroidUtilities {
FileLog.e("tmessages", e);
}
}
public static int getViewInset(View view) {
if (view == null || Build.VERSION.SDK_INT < 21) {
return 0;
}
try {
Field mAttachInfoField = View.class.getDeclaredField("mAttachInfo");
mAttachInfoField.setAccessible(true);
Object mAttachInfo = mAttachInfoField.get(view);
if (mAttachInfo != null) {
Field mStableInsetsField = mAttachInfo.getClass().getDeclaredField("mStableInsets");
mStableInsetsField.setAccessible(true);
Rect insets = (Rect)mStableInsetsField.get(mAttachInfo);
return insets.bottom;
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
return 0;
}
public static int getCurrentActionBarHeight() {
if (isTablet()) {
return dp(64);
} else if (ApplicationLoader.applicationContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
return dp(48);
} else {
return dp(56);
}
}
public static Point getRealScreenSize() {
Point size = new Point();
try {
WindowManager windowManager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Context.WINDOW_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
windowManager.getDefaultDisplay().getRealSize(size);
} else {
try {
Method mGetRawW = Display.class.getMethod("getRawWidth");
Method mGetRawH = Display.class.getMethod("getRawHeight");
size.set((Integer) mGetRawW.invoke(windowManager.getDefaultDisplay()), (Integer) mGetRawH.invoke(windowManager.getDefaultDisplay()));
} catch (Exception e) {
size.set(windowManager.getDefaultDisplay().getWidth(), windowManager.getDefaultDisplay().getHeight());
FileLog.e("tmessages", e);
}
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
return size;
}
public static void setListViewEdgeEffectColor(AbsListView listView, int color) {
if (Build.VERSION.SDK_INT >= 21) {
try {
Field field = AbsListView.class.getDeclaredField("mEdgeGlowTop");
field.setAccessible(true);
EdgeEffect mEdgeGlowTop = (EdgeEffect) field.get(listView);
if (mEdgeGlowTop != null) {
mEdgeGlowTop.setColor(color);
}
field = AbsListView.class.getDeclaredField("mEdgeGlowBottom");
field.setAccessible(true);
EdgeEffect mEdgeGlowBottom = (EdgeEffect) field.get(listView);
if (mEdgeGlowBottom != null) {
mEdgeGlowBottom.setColor(color);
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
}
public static void clearDrawableAnimation(View view) {
if (Build.VERSION.SDK_INT < 21 || view == null) {
return;
}
Drawable drawable = null;
if (view instanceof ListView) {
drawable = ((ListView) view).getSelector();
if (drawable != null) {
drawable.setState(StateSet.NOTHING);
}
} else {
drawable = view.getBackground();
if (drawable != null) {
drawable.setState(StateSet.NOTHING);
drawable.jumpToCurrentState();
}
}
}
public static Spannable replaceBold(String str) {
int start;
ArrayList<Integer> bolds = new ArrayList<Integer>();
while ((start = str.indexOf("<b>")) != -1) {
int end = str.indexOf("</b>") - 3;
str = str.replaceFirst("<b>", "").replaceFirst("</b>", "");
bolds.add(start);
bolds.add(end);
}
SpannableStringBuilder stringBuilder = new SpannableStringBuilder(str);
for (int a = 0; a < bolds.size() / 2; a++) {
TypefaceSpan span = new TypefaceSpan(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
stringBuilder.setSpan(span, bolds.get(a * 2), bolds.get(a * 2 + 1), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
}
return stringBuilder;
}
}

View File

@ -12,11 +12,11 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import org.telegram.ui.ApplicationLoader;
import org.telegram.messenger.ApplicationLoader;
public class AppStartReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
ApplicationLoader.startPushService();

View File

@ -10,9 +10,11 @@ package org.telegram.android;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentResolver;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.provider.BaseColumns;
@ -29,15 +31,17 @@ import org.telegram.messenger.TLObject;
import org.telegram.messenger.TLRPC;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.Utilities;
import org.telegram.ui.ApplicationLoader;
import org.telegram.messenger.ApplicationLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
public class ContactsController {
private Account currentAccount;
private boolean loadingContacts = false;
private static final Object loadContactsSync = new Object();
@ -48,6 +52,13 @@ public class ContactsController {
private boolean contactsBookLoaded = false;
private String lastContactsVersions = "";
private ArrayList<Integer> delayedContactsUpdate = new ArrayList<Integer>();
private String inviteText;
private boolean updatingInviteText = false;
private int loadingDeleteInfo = 0;
private int deleteAccountTTL;
private int loadingLastSeenInfo = 0;
private ArrayList<TLRPC.PrivacyRule> privacyRules = null;
public static class Contact {
public int id;
@ -75,8 +86,7 @@ public class ContactsController {
public HashMap<Integer, Contact> contactsBook = new HashMap<Integer, Contact>();
public HashMap<String, Contact> contactsBookSPhones = new HashMap<String, Contact>();
public HashMap<String, ArrayList<Contact>> contactsSectionsDict = new HashMap<String, ArrayList<Contact>>();
public ArrayList<String> sortedContactsSectionsArray = new ArrayList<String>();
public ArrayList<Contact> phoneBookContacts = new ArrayList<Contact>();
public ArrayList<TLRPC.TL_contact> contacts = new ArrayList<TLRPC.TL_contact>();
public SparseArray<TLRPC.TL_contact> contactsDict = new SparseArray<TLRPC.TL_contact>();
@ -99,11 +109,17 @@ public class ContactsController {
return localInstance;
}
public ContactsController() {
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
if (preferences.getBoolean("needGetStatuses", false)) {
reloadContactsStatuses();
}
}
public void cleanup() {
contactsBook.clear();
contactsBookSPhones.clear();
contactsSectionsDict.clear();
sortedContactsSectionsArray.clear();
phoneBookContacts.clear();
contacts.clear();
contactsDict.clear();
usersSectionsDict.clear();
@ -116,6 +132,49 @@ public class ContactsController {
contactsLoaded = false;
contactsBookLoaded = false;
lastContactsVersions = "";
loadingDeleteInfo = 0;
deleteAccountTTL = 0;
loadingLastSeenInfo = 0;
privacyRules = null;
}
public void checkInviteText() {
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))) {
updatingInviteText = true;
TLRPC.TL_help_getInviteText req = new TLRPC.TL_help_getInviteText();
req.lang_code = LocaleController.getLocaleString(Locale.getDefault());
if (req.lang_code == null || req.lang_code.length() == 0) {
req.lang_code = "en";
}
ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() {
@Override
public void run(TLObject response, TLRPC.TL_error error) {
if (error == null) {
final TLRPC.TL_help_inviteText res = (TLRPC.TL_help_inviteText)response;
if (res.message.length() != 0) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
updatingInviteText = false;
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("invitetext", res.message);
editor.putInt("invitetexttime", (int) (System.currentTimeMillis() / 1000));
editor.commit();
}
});
}
}
}
}, true, RPCRequest.RPCRequestClassGeneric | RPCRequest.RPCRequestClassFailOnServerErrors);
}
}
public String getInviteText() {
return inviteText != null ? inviteText : LocaleController.getString("InviteText", R.string.InviteText);
}
public void checkAppAccount() {
@ -419,7 +478,7 @@ public class ContactsController {
FileLog.e("tmessages", "detected account deletion!");
currentAccount = new Account(UserConfig.getCurrentUser().phone, "org.telegram.account");
am.addAccountExplicitly(currentAccount, "", null);
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
performWriteContactsToPhoneBook();
@ -546,7 +605,7 @@ public class ContactsController {
MessagesStorage.getInstance().putCachedPhoneBook(contactsMap);
}
if (!disableDeletion && !contactHashMap.isEmpty()) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (BuildVars.DEBUG_VERSION) {
@ -702,7 +761,7 @@ public class ContactsController {
}
}
});
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
updateUnregisteredContacts(contacts);
@ -763,7 +822,7 @@ public class ContactsController {
applyContactsUpdates(delayedContactsUpdate, null, null, null);
delayedContactsUpdate.clear();
}
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
synchronized (loadContactsSync) {
@ -784,13 +843,15 @@ public class ContactsController {
public void processLoadedContacts(final ArrayList<TLRPC.TL_contact> contactsArr, final ArrayList<TLRPC.User> usersArr, final int from) {
//from: 0 - from server, 1 - from db, 2 - from imported contacts
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
MessagesController.getInstance().putUsers(usersArr, from == 1);
final HashMap<Integer, TLRPC.User> usersDict = new HashMap<Integer, TLRPC.User>();
final boolean isEmpty = contactsArr.isEmpty();
if (!contacts.isEmpty()) {
for (int a = 0; a < contactsArr.size(); a++) {
TLRPC.TL_contact contact = contactsArr.get(a);
@ -928,7 +989,7 @@ public class ContactsController {
}
});
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
contacts = contactsArr;
@ -944,6 +1005,12 @@ public class ContactsController {
updateUnregisteredContacts(contactsArr);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.contactsDidLoaded);
if (from != 1 && !isEmpty) {
saveContactsLoadTime();
} else {
reloadContactsStatusesMaybe();
}
}
});
@ -953,7 +1020,7 @@ public class ContactsController {
}
if (contactsByPhonesDictFinal != null) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
Utilities.globalQueue.postRunnable(new Runnable() {
@ -978,6 +1045,27 @@ public class ContactsController {
});
}
private void reloadContactsStatusesMaybe() {
try {
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
long lastReloadStatusTime = preferences.getLong("lastReloadStatusTime", 0);
if (lastReloadStatusTime < System.currentTimeMillis() - 1000 * 60 * 60 * 24) {
reloadContactsStatuses();
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
private void saveContactsLoadTime() {
try {
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
preferences.edit().putLong("lastReloadStatusTime", System.currentTimeMillis()).commit();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
private void updateUnregisteredContacts(final ArrayList<TLRPC.TL_contact> contactsArr) {
final HashMap<String, TLRPC.TL_contact> contactsPhonesShort = new HashMap<String, TLRPC.TL_contact>();
@ -989,8 +1077,7 @@ public class ContactsController {
contactsPhonesShort.put(user.phone, value);
}
final HashMap<String, ArrayList<Contact>> sectionsPhoneDict = new HashMap<String, ArrayList<Contact>>();
final ArrayList<String> sortedSectionsPhoneArray = new ArrayList<String>();
final ArrayList<Contact> sortedPhoneBookContacts = new ArrayList<Contact>();
for (HashMap.Entry<Integer, Contact> pair : contactsBook.entrySet()) {
Contact value = pair.getValue();
int id = pair.getKey();
@ -1007,31 +1094,9 @@ public class ContactsController {
continue;
}
String key = value.first_name;
if (key.length() == 0) {
key = value.last_name;
sortedPhoneBookContacts.add(value);
}
if (key.length() == 0) {
key = "#";
if (value.phones.size() != 0) {
value.first_name = "+" + value.phones.get(0);
}
} else {
key = key.toUpperCase();
}
if (key.length() > 1) {
key = key.substring(0, 1);
}
ArrayList<Contact> arr = sectionsPhoneDict.get(key);
if (arr == null) {
arr = new ArrayList<Contact>();
sectionsPhoneDict.put(key, arr);
sortedSectionsPhoneArray.add(key);
}
arr.add(value);
}
for (HashMap.Entry<String, ArrayList<Contact>> entry : sectionsPhoneDict.entrySet()) {
Collections.sort(entry.getValue(), new Comparator<Contact>() {
Collections.sort(sortedPhoneBookContacts, new Comparator<Contact>() {
@Override
public int compare(Contact contact, Contact contact2) {
String toComapre1 = contact.first_name;
@ -1045,23 +1110,8 @@ public class ContactsController {
return toComapre1.compareTo(toComapre2);
}
});
}
Collections.sort(sortedSectionsPhoneArray, new Comparator<String>() {
@Override
public int compare(String s, String s2) {
char cv1 = s.charAt(0);
char cv2 = s2.charAt(0);
if (cv1 == '#') {
return 1;
} else if (cv2 == '#') {
return -1;
}
return s.compareTo(s2);
}
});
contactsSectionsDict = sectionsPhoneDict;
sortedContactsSectionsArray = sortedSectionsPhoneArray;
phoneBookContacts = sortedPhoneBookContacts;
}
private void buildContactsSectionsArrays(boolean sort) {
@ -1272,7 +1322,7 @@ public class ContactsController {
} else {
final ArrayList<TLRPC.TL_contact> newContacts = newC;
final ArrayList<Integer> contactsToDelete = contactsTD;
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
for (TLRPC.TL_contact contact : newContacts) {
@ -1477,7 +1527,7 @@ public class ContactsController {
}
}
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
for (TLRPC.User u : res.users) {
@ -1541,7 +1591,7 @@ public class ContactsController {
}
}
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
boolean remove = false;
@ -1564,6 +1614,132 @@ public class ContactsController {
}, true, RPCRequest.RPCRequestClassGeneric);
}
public void reloadContactsStatuses() {
saveContactsLoadTime();
MessagesController.getInstance().clearFullUsers();
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
final SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("needGetStatuses", true).commit();
TLRPC.TL_contacts_getStatuses req = new TLRPC.TL_contacts_getStatuses();
ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() {
@Override
public void run(final TLObject response, TLRPC.TL_error error) {
if (error == null) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
editor.remove("needGetStatuses").commit();
TLRPC.Vector vector = (TLRPC.Vector) response;
if (!vector.objects.isEmpty()) {
ArrayList<TLRPC.User> dbUsersStatus = new ArrayList<TLRPC.User>();
for (Object object : vector.objects) {
TLRPC.User toDbUser = new TLRPC.User();
TLRPC.TL_contactStatus status = (TLRPC.TL_contactStatus) object;
if (status == null) {
continue;
}
if (status.status instanceof TLRPC.TL_userStatusRecently) {
status.status.expires = -100;
} else if (status.status instanceof TLRPC.TL_userStatusLastWeek) {
status.status.expires = -101;
} else if (status.status instanceof TLRPC.TL_userStatusLastMonth) {
status.status.expires = -102;
}
TLRPC.User user = MessagesController.getInstance().getUser(status.user_id);
if (user != null) {
user.status = status.status;
}
toDbUser.status = status.status;
dbUsersStatus.add(toDbUser);
}
MessagesStorage.getInstance().updateUsers(dbUsersStatus, true, true, true);
}
NotificationCenter.getInstance().postNotificationName(NotificationCenter.updateInterfaces, MessagesController.UPDATE_MASK_STATUS);
}
});
}
}
});
}
public void loadPrivacySettings() {
if (loadingDeleteInfo == 0) {
loadingDeleteInfo = 1;
TLRPC.TL_account_getAccountTTL req = new TLRPC.TL_account_getAccountTTL();
ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() {
@Override
public void run(final TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (error == null) {
TLRPC.TL_accountDaysTTL ttl = (TLRPC.TL_accountDaysTTL) response;
deleteAccountTTL = ttl.days;
loadingDeleteInfo = 2;
} else {
loadingDeleteInfo = 0;
}
NotificationCenter.getInstance().postNotificationName(NotificationCenter.privacyRulesUpdated);
}
});
}
});
}
if (loadingLastSeenInfo == 0) {
loadingLastSeenInfo = 1;
TLRPC.TL_account_getPrivacy req = new TLRPC.TL_account_getPrivacy();
req.key = new TLRPC.TL_inputPrivacyKeyStatusTimestamp();
ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() {
@Override
public void run(final TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (error == null) {
TLRPC.TL_account_privacyRules rules = (TLRPC.TL_account_privacyRules) response;
MessagesController.getInstance().putUsers(rules.users, false);
privacyRules = rules.rules;
loadingLastSeenInfo = 2;
} else {
loadingLastSeenInfo = 0;
}
NotificationCenter.getInstance().postNotificationName(NotificationCenter.privacyRulesUpdated);
}
});
}
});
}
NotificationCenter.getInstance().postNotificationName(NotificationCenter.privacyRulesUpdated);
}
public void setDeleteAccountTTL(int ttl) {
deleteAccountTTL = ttl;
}
public int getDeleteAccountTTL() {
return deleteAccountTTL;
}
public boolean getLoadingDeleteInfo() {
return loadingDeleteInfo != 2;
}
public boolean getLoadingLastSeenInfo() {
return loadingLastSeenInfo != 2;
}
public ArrayList<TLRPC.PrivacyRule> getPrivacyRules() {
return privacyRules;
}
public void setPrivacyRules(ArrayList<TLRPC.PrivacyRule> rules) {
privacyRules = rules;
NotificationCenter.getInstance().postNotificationName(NotificationCenter.privacyRulesUpdated);
reloadContactsStatuses();
}
public static String formatName(String firstName, String lastName) {
String result = null;
if (LocaleController.nameDisplayOrder == 1) {

View File

@ -13,6 +13,7 @@ import java.io.InputStream;
import java.util.HashMap;
import java.util.Locale;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
@ -28,28 +29,15 @@ import android.widget.TextView;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.Utilities;
import org.telegram.ui.ApplicationLoader;
import org.telegram.messenger.ApplicationLoader;
public class Emoji {
private static HashMap<Long, DrawableInfo> rects = new HashMap<Long, DrawableInfo>();
private static int drawImgSize, bigImgSize;
private static boolean inited = false;
private static Paint placeholderPaint;
private static EmojiBitmap emojiBmp[] = new EmojiBitmap[5];
private static Bitmap emojiBmp[] = new Bitmap[5];
private static boolean loadingEmoji[] = new boolean[5];
private static int emojiFullSize;
private static class EmojiBitmap {
public int[] colors;
public int width;
public int height;
public EmojiBitmap(int[] colors, int width, int height) {
this.colors = colors;
this.width = width;
this.height = height;
}
}
private static final int[] cols = {
13, 10, 15, 10, 14
@ -125,8 +113,8 @@ public class Emoji {
0x00000000D83CDF8BL, 0x00000000D83CDF89L, 0x00000000D83CDF8AL, 0x00000000D83CDF88L, 0x00000000D83CDF8CL, 0x00000000D83DDD2EL, 0x00000000D83CDFA5L,
0x00000000D83DDCF7L, 0x00000000D83DDCF9L, 0x00000000D83DDCFCL, 0x00000000D83DDCBFL, 0x00000000D83DDCC0L, 0x00000000D83DDCBDL, 0x00000000D83DDCBEL,
0x00000000D83DDCBBL, 0x00000000D83DDCF1L, 0x000000000000260EL, 0x00000000D83DDCDEL, 0x00000000D83DDCDFL, 0x00000000D83DDCE0L, 0x00000000D83DDCE1L,
0x00000000D83DDCFAL, 0x00000000D83DDCFBL, 0x00000000D83DDD0AL, 0x00000000D83DDD09L, 0x00000000D83DDD08L, 0x00000000D83DDD07L, 0x00000000D83DDD14L, 0x00000000D83DDD15L,
0x00000000D83DDCE2L, 0x00000000D83DDCE3L, 0x00000000000023F3L, 0x000000000000231BL, 0x00000000000023F0L, 0x000000000000231AL,
0x00000000D83DDCFAL, 0x00000000D83DDCFBL, 0x00000000D83DDD0AL, 0x00000000D83DDD09L, 0x00000000D83DDD08L, 0x00000000D83DDD07L, 0x00000000D83DDD14L,
0x00000000D83DDD15L, 0x00000000D83DDCE2L, 0x00000000D83DDCE3L, 0x00000000000023F3L, 0x000000000000231BL, 0x00000000000023F0L, 0x000000000000231AL,
0x00000000D83DDD13L, 0x00000000D83DDD12L, 0x00000000D83DDD0FL, 0x00000000D83DDD10L, 0x00000000D83DDD11L, 0x00000000D83DDD0EL, 0x00000000D83DDCA1L,
0x00000000D83DDD26L, 0x00000000D83DDD06L, 0x00000000D83DDD05L, 0x00000000D83DDD0CL, 0x00000000D83DDD0BL, 0x00000000D83DDD0DL, 0x00000000D83DDEC1L, 0x00000000D83DDEC0L,
0x00000000D83DDEBFL, 0x00000000D83DDEBDL, 0x00000000D83DDD27L, 0x00000000D83DDD29L, 0x00000000D83DDD28L, 0x00000000D83DDEAAL, 0x00000000D83DDEACL,
@ -170,20 +158,19 @@ public class Emoji {
0xD83CDDF0D83CDDF7L, 0xD83CDDE9D83CDDEAL, 0xD83CDDE8D83CDDF3L, 0xD83CDDFAD83CDDF8L, 0xD83CDDEBD83CDDF7L, 0xD83CDDEAD83CDDF8L, 0xD83CDDEED83CDDF9L,
0xD83CDDF7D83CDDFAL, 0xD83CDDECD83CDDE7L},
new long[]//209
{0x00000000003120E3L, 0x00000000003220E3L, 0x00000000003320E3L, 0x00000000003420E3L, 0x00000000003520E3L, 0x00000000003620E3L, 0x00000000003720E3L, 0x00000000003820E3L,
0x00000000003920E3L, 0x00000000003020E3L, 0x00000000D83DDD1FL, 0x00000000D83DDD22L, 0x00000000002320E3L, 0x00000000D83DDD23L, 0x0000000000002B06L,
0x0000000000002B07L, 0x0000000000002B05L, 0x00000000000027A1L, 0x00000000D83DDD20L, 0x00000000D83DDD21L, 0x00000000D83DDD24L, 0x0000000000002197L,
0x0000000000002196L, 0x0000000000002198L, 0x0000000000002199L, 0x0000000000002194L, 0x0000000000002195L, 0x00000000D83DDD04L, 0x00000000000025C0L,
0x00000000000025B6L, 0x00000000D83DDD3CL, 0x00000000D83DDD3DL, 0x00000000000021A9L, 0x00000000000021AAL, 0x0000000000002139L, 0x00000000000023EAL,
0x00000000000023E9L, 0x00000000000023EBL, 0x00000000000023ECL, 0x0000000000002935L, 0x0000000000002934L, 0x00000000D83CDD97L, 0x00000000D83DDD00L,
0x00000000D83DDD01L, 0x00000000D83DDD02L, 0x00000000D83CDD95L, 0x00000000D83CDD99L, 0x00000000D83CDD92L, 0x00000000D83CDD93L, 0x00000000D83CDD96L,
0x00000000D83DDCF6L, 0x00000000D83CDFA6L, 0x00000000D83CDE01L, 0x00000000D83CDE2FL, 0x00000000D83CDE33L, 0x00000000D83CDE35L, 0x00000000D83CDE32L,
0x00000000D83CDE34L, 0x00000000D83CDE50L, 0x00000000D83CDE39L, 0x00000000D83CDE3AL, 0x00000000D83CDE36L, 0x00000000D83CDE1AL,
{0x00000000003120E3L, 0x00000000003220E3L, 0x00000000003320E3L, 0x00000000003420E3L, 0x00000000003520E3L, 0x00000000003620E3L, 0x00000000003720E3L,
0x00000000003820E3L, 0x00000000003920E3L, 0x00000000003020E3L, 0x00000000D83DDD1FL, 0x00000000D83DDD22L, 0x00000000002320E3L, 0x00000000D83DDD23L,
0x0000000000002B06L, 0x0000000000002B07L, 0x0000000000002B05L, 0x00000000000027A1L, 0x00000000D83DDD20L, 0x00000000D83DDD21L, 0x00000000D83DDD24L,
0x0000000000002197L, 0x0000000000002196L, 0x0000000000002198L, 0x0000000000002199L, 0x0000000000002194L, 0x0000000000002195L, 0x00000000D83DDD04L,
0x00000000000025C0L, 0x00000000000025B6L, 0x00000000D83DDD3CL, 0x00000000D83DDD3DL, 0x00000000000021A9L, 0x00000000000021AAL, 0x0000000000002139L,
0x00000000000023EAL, 0x00000000000023E9L, 0x00000000000023EBL, 0x00000000000023ECL, 0x0000000000002935L, 0x0000000000002934L, 0x00000000D83CDD97L,
0x00000000D83DDD00L, 0x00000000D83DDD01L, 0x00000000D83DDD02L, 0x00000000D83CDD95L, 0x00000000D83CDD99L, 0x00000000D83CDD92L, 0x00000000D83CDD93L,
0x00000000D83CDD96L, 0x00000000D83DDCF6L, 0x00000000D83CDFA6L, 0x00000000D83CDE01L, 0x00000000D83CDE2FL, 0x00000000D83CDE33L, 0x00000000D83CDE35L,
0x00000000D83CDE32L, 0x00000000D83CDE34L, 0x00000000D83CDE50L, 0x00000000D83CDE39L, 0x00000000D83CDE3AL, 0x00000000D83CDE36L, 0x00000000D83CDE1AL,
0x00000000D83DDEBBL, 0x00000000D83DDEB9L, 0x00000000D83DDEBAL, 0x00000000D83DDEBCL, 0x00000000D83DDEBEL, 0x00000000D83DDEB0L, 0x00000000D83DDEAEL,
0x00000000D83CDD7FL, 0x000000000000267FL, 0x00000000D83DDEADL, 0x00000000D83CDE37L, 0x00000000D83CDE38L, 0x00000000D83CDE02L, 0x00000000000024C2L,
0x00000000D83DDEC2L, 0x00000000D83DDEC4L, 0x00000000D83DDEC5L, 0x00000000D83DDEC3L,
0x00000000D83CDE51L, 0x0000000000003299L, 0x0000000000003297L, 0x00000000D83CDD91L, 0x00000000D83CDD98L, 0x00000000D83CDD94L, 0x00000000D83DDEABL,
0x00000000D83DDEC2L, 0x00000000D83DDEC4L, 0x00000000D83DDEC5L, 0x00000000D83DDEC3L, 0x00000000D83CDE51L, 0x0000000000003299L, 0x0000000000003297L,
0x00000000D83CDD91L, 0x00000000D83CDD98L, 0x00000000D83CDD94L, 0x00000000D83DDEABL,
0x00000000D83DDD1EL, 0x00000000D83DDCF5L, 0x00000000D83DDEAFL, 0x00000000D83DDEB1L, 0x00000000D83DDEB3L, 0x00000000D83DDEB7L, 0x00000000D83DDEB8L,
0x00000000000026D4L, 0x0000000000002733L, 0x0000000000002747L, 0x000000000000274EL, 0x0000000000002705L, 0x0000000000002734L, 0x00000000D83DDC9FL,
0x00000000D83CDD9AL, 0x00000000D83DDCF3L, 0x00000000D83DDCF4L, 0x00000000D83CDD70L, 0x00000000D83CDD71L, 0x00000000D83CDD8EL, 0x00000000D83CDD7EL,
@ -204,6 +191,7 @@ public class Emoji {
0x00000000D83DDD34L, 0x00000000D83DDD35L, 0x00000000D83DDD3BL, 0x00000000D83DDD36L, 0x00000000D83DDD37L, 0x00000000D83DDD38L, 0x00000000D83DDD39L}};
static {
int emojiFullSize;
if (AndroidUtilities.density <= 1.0f) {
emojiFullSize = 30;
} else if (AndroidUtilities.density <= 1.5f) {
@ -222,7 +210,7 @@ public class Emoji {
for (int j = 1; j < data.length; j++) {
for (int i = 0; i < data[j].length; i++) {
Rect rect = new Rect((i % cols[j - 1]) * emojiFullSize, (i / cols[j - 1]) * emojiFullSize, emojiFullSize, emojiFullSize);
Rect rect = new Rect((i % cols[j - 1]) * emojiFullSize, (i / cols[j - 1]) * emojiFullSize, (i % cols[j - 1] + 1) * emojiFullSize, (i / cols[j - 1] + 1) * emojiFullSize);
rects.put(data[j][i], new DrawableInfo(rect, (byte)(j - 1)));
}
}
@ -260,8 +248,10 @@ public class Emoji {
int width = opts.outWidth / imageResize;
int height = opts.outHeight / imageResize;
int[] bitmap = new int[width * height];
Utilities.loadBitmap(imageFile.getAbsolutePath(), bitmap, imageResize, 0, width, height);
int stride = width * 4;
final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Utilities.loadBitmap(imageFile.getAbsolutePath(), bitmap, imageResize, width, height, stride);
imageName = String.format(Locale.US, "emoji%.01fx_a_%d.jpg", scale, page);
imageFile = ApplicationLoader.applicationContext.getFileStreamPath(imageName);
@ -271,13 +261,12 @@ public class Emoji {
is.close();
}
Utilities.loadBitmap(imageFile.getAbsolutePath(), bitmap, imageResize, 0, width, height);
Utilities.loadBitmap(imageFile.getAbsolutePath(), bitmap, imageResize, width, height, stride);
final EmojiBitmap emojiBitmap = new EmojiBitmap(bitmap, width, height);
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
emojiBmp[page] = emojiBitmap;
emojiBmp[page] = bitmap;
NotificationCenter.getInstance().postNotificationName(NotificationCenter.emojiDidLoaded);
}
});
@ -310,7 +299,7 @@ public class Emoji {
}
}
public static Drawable getEmojiDrawable(long code) {
public static EmojiDrawable getEmojiDrawable(long code) {
DrawableInfo info = rects.get(code);
if (info == null) {
FileLog.e("tmessages", "No emoji drawable for code " + String.format("%016X", code));
@ -322,7 +311,7 @@ public class Emoji {
}
public static Drawable getEmojiBigDrawable(long code) {
EmojiDrawable ed = (EmojiDrawable)getEmojiDrawable(code);
EmojiDrawable ed = getEmojiDrawable(code);
if (ed == null) {
return null;
}
@ -333,39 +322,44 @@ public class Emoji {
public static class EmojiDrawable extends Drawable {
private DrawableInfo info;
boolean fullSize = false;
private static Paint paint;
static {
paint = new Paint();
paint.setFlags(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
}
private boolean fullSize = false;
private static Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
public EmojiDrawable(DrawableInfo i) {
info = i;
}
public DrawableInfo getDrawableInfo() {
return info;
}
public Rect getDrawRect() {
Rect b = copyBounds();
int cX = b.centerX(), cY = b.centerY();
b.left = cX - (fullSize ? bigImgSize : drawImgSize) / 2;
b.right = cX + (fullSize ? bigImgSize : drawImgSize) / 2;
b.top = cY - (fullSize ? bigImgSize : drawImgSize) / 2;
b.bottom = cY + (fullSize ? bigImgSize : drawImgSize) / 2;
return b;
}
@Override
public void draw(Canvas canvas) {
EmojiBitmap bitmap = emojiBmp[info.page];
if (bitmap == null) {
if (emojiBmp[info.page] == null) {
loadEmojiAsync(info.page);
canvas.drawRect(getBounds(), placeholderPaint);
return;
}
float scale = 1;
int offset = 0;
Rect b;
if (fullSize) {
scale = (float) bigImgSize / (float) emojiFullSize;
offset = (getBounds().width() - bigImgSize) / 2;
b = getDrawRect();
} else {
scale = (float) getBounds().width() / (float) emojiFullSize;
b = getBounds();
}
canvas.save();
canvas.scale(scale, scale);
canvas.drawBitmap(bitmap.colors, info.rect.top * bitmap.width + info.rect.left, bitmap.width, offset, offset, info.rect.right, info.rect.bottom, true, paint);
canvas.restore();
if (!canvas.quickReject(b.left, b.top, b.right, b.bottom, Canvas.EdgeType.AA)) {
canvas.drawBitmap(emojiBmp[info.page], info.rect, b, paint);
}
}
@Override
@ -424,7 +418,7 @@ public class Emoji {
} else if (buf > 0 && (c & 0xF000) == 0xD000) {
buf <<= 16;
buf |= c;
Drawable d = Emoji.getEmojiDrawable(buf);
EmojiDrawable d = Emoji.getEmojiDrawable(buf);
if (d != null) {
EmojiSpan span = new EmojiSpan(d, DynamicDrawableSpan.ALIGN_BOTTOM, size, fontMetrics);
emojiCount++;
@ -442,7 +436,7 @@ public class Emoji {
buf = c2;
buf <<= 16;
buf |= c;
Drawable d = Emoji.getEmojiDrawable(buf);
EmojiDrawable d = Emoji.getEmojiDrawable(buf);
if (d != null) {
EmojiSpan span = new EmojiSpan(d, DynamicDrawableSpan.ALIGN_BOTTOM, size, fontMetrics);
emojiCount++;
@ -452,7 +446,7 @@ public class Emoji {
}
}
} else if (inArray(c, emojiChars)) {
Drawable d = Emoji.getEmojiDrawable(c);
EmojiDrawable d = Emoji.getEmojiDrawable(c);
if (d != null) {
EmojiSpan span = new EmojiSpan(d, DynamicDrawableSpan.ALIGN_BOTTOM, size, fontMetrics);
emojiCount++;
@ -472,9 +466,9 @@ public class Emoji {
public static class EmojiSpan extends ImageSpan {
private Paint.FontMetricsInt fontMetrics = null;
int size = AndroidUtilities.dp(20);
private int size = AndroidUtilities.dp(20);
public EmojiSpan(Drawable d, int verticalAlignment, int s, Paint.FontMetricsInt original) {
public EmojiSpan(EmojiDrawable d, int verticalAlignment, int s, Paint.FontMetricsInt original) {
super(d, verticalAlignment);
fontMetrics = original;
if (original != null) {

View File

@ -16,7 +16,7 @@ import android.content.Intent;
import org.json.JSONObject;
import org.telegram.messenger.ConnectionsManager;
import org.telegram.messenger.FileLog;
import org.telegram.ui.ApplicationLoader;
import org.telegram.messenger.ApplicationLoader;
public class GcmBroadcastReceiver extends BroadcastReceiver {
@ -27,7 +27,7 @@ public class GcmBroadcastReceiver extends BroadcastReceiver {
FileLog.d("tmessages", "GCM received intent: " + intent);
if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
ApplicationLoader.postInitApplication();

View File

@ -31,7 +31,7 @@ import org.telegram.messenger.FileLog;
import org.telegram.messenger.TLRPC;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.Utilities;
import org.telegram.ui.ApplicationLoader;
import org.telegram.messenger.ApplicationLoader;
import java.io.ByteArrayOutputStream;
import java.io.File;
@ -307,7 +307,7 @@ public class ImageLoader {
}
private void onPostExecute(final BitmapDrawable bitmapDrawable) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (bitmapDrawable != null && memCache.get(cacheImage.key) == null) {
@ -518,7 +518,7 @@ public class ImageLoader {
if (lastProgressUpdateTime == 0 || lastProgressUpdateTime < currentTime - 500) {
lastProgressUpdateTime = currentTime;
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.FileUploadProgressChanged, location, progress, isEncrypted);
@ -549,7 +549,7 @@ public class ImageLoader {
@Override
public void fileDidLoaded(final String location, final File finalFile, final File tempFile) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (location != null) {
@ -567,7 +567,7 @@ public class ImageLoader {
@Override
public void fileDidFailedLoad(final String location, final int state) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
ImageLoader.this.fileDidFailedLoad(location);
@ -581,7 +581,7 @@ public class ImageLoader {
long currentTime = System.currentTimeMillis();
if (lastProgressUpdateTime == 0 || lastProgressUpdateTime < currentTime - 500) {
lastProgressUpdateTime = currentTime;
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.FileLoadProgressChanged, location, progress);
@ -601,7 +601,7 @@ public class ImageLoader {
}
};
if (Intent.ACTION_MEDIA_UNMOUNTED.equals(intent.getAction())) {
AndroidUtilities.RunOnUIThread(r, 1000);
AndroidUtilities.runOnUIThread(r, 1000);
} else {
r.run();
}
@ -816,7 +816,7 @@ public class ImageLoader {
}
public void replaceImageInCache(final String oldKey, final String newKey) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
ArrayList<String> arr = memCache.getFilterKeys(oldKey);
@ -1117,9 +1117,9 @@ public class ImageLoader {
return b;
}
private static TLRPC.PhotoSize scaleAndSaveImageInternal(Bitmap bitmap, int w, int h, float photoW, float photoH, float scaleFactor, int quality, boolean cache) throws Exception {
private static TLRPC.PhotoSize scaleAndSaveImageInternal(Bitmap bitmap, int w, int h, float photoW, float photoH, float scaleFactor, int quality, boolean cache, boolean scaleAnyway) throws Exception {
Bitmap scaledBitmap = null;
if (scaleFactor > 1) {
if (scaleFactor > 1 || scaleAnyway) {
scaledBitmap = Bitmap.createScaledBitmap(bitmap, w, h, true);
} else {
scaledBitmap = bitmap;
@ -1171,6 +1171,10 @@ public class ImageLoader {
}
public static TLRPC.PhotoSize scaleAndSaveImage(Bitmap bitmap, float maxWidth, float maxHeight, int quality, boolean cache) {
return scaleAndSaveImage(bitmap, maxWidth, maxHeight, quality, cache, 0, 0);
}
public static TLRPC.PhotoSize scaleAndSaveImage(Bitmap bitmap, float maxWidth, float maxHeight, int quality, boolean cache, int minWidth, int minHeight) {
if (bitmap == null) {
return null;
}
@ -1179,7 +1183,12 @@ public class ImageLoader {
if (photoW == 0 || photoH == 0) {
return null;
}
boolean scaleAnyway = false;
float scaleFactor = Math.max(photoW / maxWidth, photoH / maxHeight);
if (scaleFactor < 1 && minWidth != 0 && minHeight != 0) {
scaleFactor = Math.max(photoW / minWidth, photoH / minHeight);
scaleAnyway = true;
}
int w = (int)(photoW / scaleFactor);
int h = (int)(photoH / scaleFactor);
if (h == 0 || w == 0) {
@ -1187,13 +1196,13 @@ public class ImageLoader {
}
try {
return scaleAndSaveImageInternal(bitmap, w, h, photoW, photoH, scaleFactor, quality, cache);
return scaleAndSaveImageInternal(bitmap, w, h, photoW, photoH, scaleFactor, quality, cache, scaleAnyway);
} catch (Throwable e) {
FileLog.e("tmessages", e);
ImageLoader.getInstance().clearMemory();
System.gc();
try {
return scaleAndSaveImageInternal(bitmap, w, h, photoW, photoH, scaleFactor, quality, cache);
return scaleAndSaveImageInternal(bitmap, w, h, photoW, photoH, scaleFactor, quality, cache, scaleAnyway);
} catch (Throwable e2) {
FileLog.e("tmessages", e2);
return null;

View File

@ -139,7 +139,7 @@ public class ImageReceiver {
if (roundRadius != 0) {
bitmapShader = new BitmapShader(bitmap.getBitmap(), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
roundPaint.setShader(bitmapShader);
bitmapRect.set(0, 0, bitmap.getIntrinsicWidth(), bitmap.getIntrinsicHeight());
bitmapRect.set(0, 0, bitmap.getBitmap().getWidth(), bitmap.getBitmap().getHeight());
}
if (parentView != null) {
parentView.invalidate();
@ -225,11 +225,13 @@ public class ImageReceiver {
if (bitmapDrawable != null) {
if (bitmapShader != null) {
drawRegion.set(imageX, imageY, imageX + imageW, imageY + imageH);
roundRect.set(imageX, imageY, imageX + imageW, imageY + imageH);
if (isVisible) {
roundRect.set(drawRegion);
shaderMatrix.reset();
shaderMatrix.setScale(1.5f, 1.5f);
shaderMatrix.setRectToRect(bitmapRect, roundRect, Matrix.ScaleToFit.FILL);
bitmapShader.setLocalMatrix(shaderMatrix);
canvas.drawRoundRect(roundRect, roundRadius, roundRadius, roundPaint);
}
} else {
int bitmapW = bitmapDrawable.getIntrinsicWidth();
int bitmapH = bitmapDrawable.getIntrinsicHeight();
@ -412,10 +414,12 @@ public class ImageReceiver {
public void setRoundRadius(int value) {
roundRadius = value;
if (roundRadius != 0) {
if (roundPaint == null) {
roundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
roundRect = new RectF();
shaderMatrix = new Matrix();
bitmapRect = new RectF();
}
} else {
roundPaint = null;
roundRect = null;
@ -423,4 +427,8 @@ public class ImageReceiver {
bitmapRect = null;
}
}
public int getRoundRadius() {
return roundRadius;
}
}

View File

@ -18,12 +18,13 @@ import android.content.res.Configuration;
import android.text.format.DateFormat;
import android.util.Xml;
import org.telegram.android.time.FastDateFormat;
import org.telegram.messenger.ConnectionsManager;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.R;
import org.telegram.messenger.TLRPC;
import org.telegram.messenger.Utilities;
import org.telegram.ui.ApplicationLoader;
import org.telegram.messenger.ApplicationLoader;
import org.xmlpull.v1.XmlPullParser;
import java.io.File;
@ -781,8 +782,22 @@ public class LocaleController {
}
public static String formatUserStatus(TLRPC.User user) {
if (user != null && user.status != null && user.status.expires == 0) {
if (user.status instanceof TLRPC.TL_userStatusRecently) {
user.status.expires = -100;
} else if (user.status instanceof TLRPC.TL_userStatusLastWeek) {
user.status.expires = -101;
} else if (user.status instanceof TLRPC.TL_userStatusLastMonth) {
user.status.expires = -102;
}
}
if (user != null && user.status != null && user.status.expires <= 0) {
if (MessagesController.getInstance().onlinePrivacy.containsKey(user.id)) {
return getString("Online", R.string.Online);
}
}
if (user == null || user.status == null || user.status.expires == 0 || user instanceof TLRPC.TL_userDeleted || user instanceof TLRPC.TL_userEmpty) {
return getString("Offline", R.string.Offline);
return getString("ALongTimeAgo", R.string.ALongTimeAgo);
} else {
int currentTime = ConnectionsManager.getInstance().getCurrentTime();
if (user.status.expires > currentTime) {
@ -790,6 +805,12 @@ public class LocaleController {
} else {
if (user.status.expires == -1) {
return getString("Invisible", R.string.Invisible);
} else if (user.status.expires == -100) {
return getString("Lately", R.string.Lately);
} else if (user.status.expires == -101) {
return getString("WithinAWeek", R.string.WithinAWeek);
} else if (user.status.expires == -102) {
return getString("WithinAMonth", R.string.WithinAMonth);
} else {
return formatDateOnline(user.status.expires);
}

View File

@ -38,9 +38,7 @@ import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.Vibrator;
import android.provider.MediaStore;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
import org.telegram.android.video.InputSurface;
import org.telegram.android.video.MP4Builder;
@ -54,15 +52,14 @@ import org.telegram.messenger.R;
import org.telegram.messenger.TLRPC;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.Utilities;
import org.telegram.ui.ApplicationLoader;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.ui.Cells.ChatMediaCell;
import org.telegram.ui.Views.GifDrawable;
import org.telegram.ui.Components.GifDrawable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
@ -273,7 +270,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
});
}
recordQueue.postRunnable(recordRunnable);
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.recordProgressChanged, System.currentTimeMillis() - recordStartTime);
@ -451,7 +448,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
@Override
public void run() {
synchronized (sync) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (playingMessageObject != null && (audioPlayer != null || audioTrackPlayer != null) && !isPaused) {
@ -739,29 +736,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
public void processMediaObserver(Uri uri) {
try {
int width = 0;
int height = 0;
try {
WindowManager windowManager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Context.WINDOW_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Point size = new Point();
windowManager.getDefaultDisplay().getRealSize(size);
width = size.x;
height = size.y;
} else {
try {
Method mGetRawW = Display.class.getMethod("getRawWidth");
Method mGetRawH = Display.class.getMethod("getRawHeight");
width = (Integer) mGetRawW.invoke(windowManager.getDefaultDisplay());
height = (Integer) mGetRawH.invoke(windowManager.getDefaultDisplay());
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
Point size = AndroidUtilities.getRealScreenSize();
Cursor cursor = ApplicationLoader.applicationContext.getContentResolver().query(uri, mediaProjections, null, null, "date_added DESC LIMIT 1");
final ArrayList<Long> screenshotDates = new ArrayList<Long>();
@ -791,7 +766,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
photoW = bmOptions.outWidth;
photoH = bmOptions.outHeight;
}
if (photoW <= 0 || photoH <= 0 || (photoW == width && photoH == height || photoH == width && photoW == height)) {
if (photoW <= 0 || photoH <= 0 || (photoW == size.x && photoH == size.y || photoH == size.x && photoW == size.y)) {
screenshotDates.add(date);
}
} catch (Exception e) {
@ -802,7 +777,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
cursor.close();
}
if (!screenshotDates.isEmpty()) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.screenshotTook);
@ -834,7 +809,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
}
}
if (send) {
SendMessagesHelper.getInstance().sendScreenshotMessage(lastSecretChat, lastSecretChatVisibleMessages, null);
SecretChatHelper.getInstance().sendScreenshotMessage(lastSecretChat, lastSecretChatVisibleMessages, null);
}
}
@ -1060,7 +1035,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
if (count > 0) {
final long pcm = buffer.pcmOffset;
final int marker = buffer.finished == 1 ? buffer.size : -1;
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
lastPlayPcm = pcm;
@ -1149,7 +1124,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
freePlayerBuffers.addAll(usedPlayerBuffers);
usedPlayerBuffers.clear();
}
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (!isPaused) {
@ -1404,7 +1379,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
@Override
public void run() {
if (audioRecorder != null) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.recordStartError);
@ -1425,7 +1400,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
try {
if (startRecord(recordingAudioFile.getAbsolutePath()) == 0) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.recordStartError);
@ -1453,7 +1428,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
FileLog.e("tmessages", e2);
}
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.recordStartError);
@ -1463,7 +1438,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
}
recordQueue.postRunnable(recordRunnable);
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.recordStarted);
@ -1481,7 +1456,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
@Override
public void run() {
stopRecord();
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
audioToSend.date = ConnectionsManager.getInstance().getCurrentTime();
@ -1536,7 +1511,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
} catch (Exception e) {
FileLog.e("tmessages", e);
}
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.recordStopped);
@ -1567,6 +1542,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
if (sourceFile.exists()) {
ProgressDialog progressDialog = null;
if (context != null) {
try {
progressDialog = new ProgressDialog(context);
progressDialog.setMessage(LocaleController.getString("Loading", R.string.Loading));
progressDialog.setCanceledOnTouchOutside(false);
@ -1574,6 +1550,9 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMax(100);
progressDialog.show();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
final ProgressDialog finalProgress = progressDialog;
@ -1609,7 +1588,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
if (lastProgress <= System.currentTimeMillis() - 500) {
lastProgress = System.currentTimeMillis();
final int progress = (int) ((float) a / (float) size * 100);
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
try {
@ -1641,7 +1620,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
FileLog.e("tmessages", e);
}
if (finalProgress != null) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
try {
@ -1915,7 +1894,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
}
}
final Integer cameraAlbumIdFinal = cameraAlbumId;
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.albumsDidLoaded, guid, albumsSorted, cameraAlbumIdFinal);
@ -2037,7 +2016,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
if (firstWrite) {
videoConvertFirstWrite = false;
}
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (error) {

View File

@ -25,7 +25,7 @@ import org.telegram.messenger.FileLog;
import org.telegram.messenger.TLRPC;
import org.telegram.messenger.R;
import org.telegram.messenger.UserConfig;
import org.telegram.ui.Views.URLSpanNoUnderline;
import org.telegram.ui.Components.URLSpanNoUnderline;
import java.util.AbstractMap;
import java.util.ArrayList;
@ -345,7 +345,9 @@ public class MessageObject {
int dateMonth = rightNow.get(Calendar.MONTH);
dateKey = String.format("%d_%02d_%02d", dateYear, dateMonth, dateDay);
if (preview != 0) {
generateLayout();
}
generateThumbs(false, preview);
}
@ -450,6 +452,60 @@ public class MessageObject {
return "";
}
private boolean containsUrls(String message) {
if (message == null || message.length() < 3 || message.length() > 1024 * 20) {
return false;
}
boolean containsSomething = false;
int length = message.length();
int digitsInRow = 0;
int schemeSequence = 0;
int dotSequence = 0;
char lastChar = 0;
for (int i = 0; i < length; i++) {
char c = message.charAt(i);
if (c >= '0' && c <= '9') {
digitsInRow++;
if (digitsInRow >= 6) {
return true;
}
schemeSequence = 0;
dotSequence = 0;
} else if (c == ':') {
if (schemeSequence == 0) {
schemeSequence = 1;
} else {
schemeSequence = 0;
}
} else if (c == '/') {
if (schemeSequence == 2) {
return true;
}
if (schemeSequence == 1) {
schemeSequence++;
} else {
schemeSequence = 0;
}
} else if (c == '.') {
if (dotSequence == 0 && lastChar != ' ') {
dotSequence++;
} else {
dotSequence = 0;
}
} else if (c != ' ' && lastChar == '.' && dotSequence == 1) {
return true;
}
lastChar = c;
}
return false;
}
private void generateLayout() {
if (type != 0 || messageOwner.to_id == null || messageText == null || messageText.length() == 0) {
return;
@ -457,11 +513,11 @@ public class MessageObject {
textLayoutBlocks = new ArrayList<TextLayoutBlock>();
if (messageText instanceof Spannable) {
if (messageOwner.message != null && messageOwner.message.contains(".") && (messageOwner.message.contains(".com") || messageOwner.message.contains("http") || messageOwner.message.contains(".ru") || messageOwner.message.contains(".org") || messageOwner.message.contains(".net"))) {
if (messageText instanceof Spannable && containsUrls(messageOwner.message)) {
if (messageOwner.message.length() < 100) {
Linkify.addLinks((Spannable) messageText, Linkify.WEB_URLS | Linkify.PHONE_NUMBERS);
} else {
Linkify.addLinks((Spannable) messageText, Linkify.WEB_URLS);
} else if (messageText.length() < 100) {
Linkify.addLinks((Spannable)messageText, Linkify.WEB_URLS | Linkify.EMAIL_ADDRESSES | Linkify.PHONE_NUMBERS);
}
}
@ -574,10 +630,7 @@ public class MessageObject {
}
if (lineWidth > maxWidth + 100) {
int start = block.textLayout.getLineStart(n);
int end = block.textLayout.getLineEnd(n);
CharSequence text = block.textLayout.getText().subSequence(start, end);
continue;
lineWidth = maxWidth;
}
try {

View File

@ -10,7 +10,6 @@ package org.telegram.android;
import android.content.Context;
import android.content.SharedPreferences;
import android.text.Html;
import android.text.TextUtils;
import android.util.SparseArray;
@ -29,7 +28,7 @@ import org.telegram.messenger.TLObject;
import org.telegram.messenger.TLRPC;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.Utilities;
import org.telegram.ui.ApplicationLoader;
import org.telegram.messenger.ApplicationLoader;
import java.io.File;
import java.util.ArrayList;
@ -41,7 +40,7 @@ import java.util.Map;
import java.util.concurrent.Semaphore;
public class MessagesStorage {
public DispatchQueue storageQueue = new DispatchQueue("storageQueue");
private DispatchQueue storageQueue = new DispatchQueue("storageQueue");
private SQLiteDatabase database;
private File cacheFile;
private BuffersStorage buffersStorage = new BuffersStorage(false);
@ -77,6 +76,18 @@ public class MessagesStorage {
openDatabase();
}
public SQLiteDatabase getDatabase() {
return database;
}
public DispatchQueue getStorageQueue() {
return storageQueue;
}
public BuffersStorage getBuffersStorage() {
return buffersStorage;
}
public void openDatabase() {
cacheFile = new File(ApplicationLoader.applicationContext.getFilesDir(), "cache4.db");
@ -93,7 +104,7 @@ public class MessagesStorage {
database.executeFast("CREATE TABLE users(uid INTEGER PRIMARY KEY, name TEXT, status INTEGER, data BLOB)").stepThis().dispose();
database.executeFast("CREATE TABLE messages(mid INTEGER PRIMARY KEY, uid INTEGER, read_state INTEGER, send_state INTEGER, date INTEGER, data BLOB, out INTEGER, ttl INTEGER, media INTEGER)").stepThis().dispose();
database.executeFast("CREATE TABLE chats(uid INTEGER PRIMARY KEY, name TEXT, data BLOB)").stepThis().dispose();
database.executeFast("CREATE TABLE enc_chats(uid INTEGER PRIMARY KEY, user INTEGER, name TEXT, data BLOB, g BLOB, authkey BLOB, ttl INTEGER, layer INTEGER, seq_in INTEGER, seq_out INTEGER)").stepThis().dispose();
database.executeFast("CREATE TABLE enc_chats(uid INTEGER PRIMARY KEY, user INTEGER, name TEXT, data BLOB, g BLOB, authkey BLOB, ttl INTEGER, layer INTEGER, seq_in INTEGER, seq_out INTEGER, use_count INTEGER, exchange_id INTEGER, key_date INTEGER, fprint INTEGER, fauthkey BLOB, khash BLOB)").stepThis().dispose();
database.executeFast("CREATE TABLE dialogs(did INTEGER PRIMARY KEY, date INTEGER, unread_count INTEGER, last_mid INTEGER)").stepThis().dispose();
database.executeFast("CREATE TABLE chat_settings(uid INTEGER PRIMARY KEY, participants BLOB)").stepThis().dispose();
database.executeFast("CREATE TABLE contacts(uid INTEGER PRIMARY KEY, mutual INTEGER)").stepThis().dispose();
@ -110,6 +121,7 @@ public class MessagesStorage {
database.executeFast("CREATE TABLE download_queue(uid INTEGER, type INTEGER, date INTEGER, data BLOB, PRIMARY KEY (uid, type));").stepThis().dispose();
database.executeFast("CREATE TABLE dialog_settings(did INTEGER PRIMARY KEY, flags INTEGER);").stepThis().dispose();
database.executeFast("CREATE TABLE messages_seq(mid INTEGER PRIMARY KEY, seq_in INTEGER, seq_out INTEGER);").stepThis().dispose();
//database.executeFast("CREATE TABLE secret_holes(uid INTEGER, seq_in INTEGER, seq_out INTEGER, data BLOB, PRIMARY KEY (uid, seq_in, seq_out));").stepThis().dispose();
//database.executeFast("CREATE TABLE attach_data(uid INTEGER, id INTEGER, data BLOB, PRIMARY KEY (uid, id))").stepThis().dispose();
@ -118,6 +130,9 @@ public class MessagesStorage {
database.executeFast("CREATE TABLE sent_files_v2(uid TEXT, type INTEGER, data BLOB, PRIMARY KEY (uid, type))").stepThis().dispose();
//database.executeFast("CREATE TABLE messages_holes(uid INTEGER, start INTEGER, end INTEGER, PRIMARY KEY(uid, start));").stepThis().dispose();
//database.executeFast("CREATE INDEX IF NOT EXISTS type_uid_end_messages_holes ON messages_holes(uid, end);").stepThis().dispose();
database.executeFast("CREATE INDEX IF NOT EXISTS type_date_idx_download_queue ON download_queue(type, date);").stepThis().dispose();
database.executeFast("CREATE INDEX IF NOT EXISTS mid_idx_randoms ON randoms(mid);").stepThis().dispose();
@ -141,7 +156,7 @@ public class MessagesStorage {
database.executeFast("CREATE INDEX IF NOT EXISTS seq_idx_messages_seq ON messages_seq(seq_in, seq_out);").stepThis().dispose();
database.executeFast("PRAGMA user_version = 7").stepThis().dispose();
database.executeFast("PRAGMA user_version = 10").stepThis().dispose();
} else {
try {
SQLiteCursor cursor = database.queryFinalized("SELECT seq, pts, date, qts, lsv, sg, pbytes FROM params WHERE id = 1");
@ -173,7 +188,7 @@ public class MessagesStorage {
}
int version = database.executeInt("PRAGMA user_version");
if (version < 7) {
if (version < 10) {
updateDbToLastVersion(version);
}
}
@ -303,6 +318,27 @@ public class MessagesStorage {
database.executeFast("PRAGMA user_version = 7").stepThis().dispose();
version = 7;
}
/*if (version == 7 && version < 8) {
database.executeFast("CREATE TABLE IF NOT EXISTS secret_holes(uid INTEGER, seq_in INTEGER, seq_out INTEGER, data BLOB, PRIMARY KEY (uid, seq_in, seq_out));").stepThis().dispose();
database.executeFast("PRAGMA user_version = 8").stepThis().dispose();
version = 8;
}*/
/*if ((version == 7 || version == 8) && version < 9) {
database.executeFast("CREATE TABLE IF NOT EXISTS messages_holes(uid INTEGER, start INTEGER, end INTEGER, PRIMARY KEY(uid, start));").stepThis().dispose();
database.executeFast("CREATE INDEX IF NOT EXISTS type_uid_end_messages_holes ON messages_holes(uid, end);").stepThis().dispose();
database.executeFast("PRAGMA user_version = 9").stepThis().dispose();
version = 9;
}*/
if ((version == 7 || version == 8 || version == 9) && version < 10) {
database.executeFast("ALTER TABLE enc_chats ADD COLUMN use_count INTEGER default 0").stepThis().dispose();
database.executeFast("ALTER TABLE enc_chats ADD COLUMN exchange_id INTEGER default 0").stepThis().dispose();
database.executeFast("ALTER TABLE enc_chats ADD COLUMN key_date INTEGER default 0").stepThis().dispose();
database.executeFast("ALTER TABLE enc_chats ADD COLUMN fprint INTEGER default 0").stepThis().dispose();
database.executeFast("ALTER TABLE enc_chats ADD COLUMN fauthkey BLOB default NULL").stepThis().dispose();
database.executeFast("ALTER TABLE enc_chats ADD COLUMN khash BLOB default NULL").stepThis().dispose();
database.executeFast("PRAGMA user_version = 10").stepThis().dispose();
version = 10;
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
@ -511,7 +547,7 @@ public class MessagesStorage {
}
}
Collections.reverse(messages);
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationsController.getInstance().processLoadedUnreadMessages(pushDialogs, messages, users, chats, encryptedChats);
@ -665,6 +701,7 @@ public class MessagesStorage {
}
} else {
database.executeFast("DELETE FROM enc_chats WHERE uid = " + high_id).stepThis().dispose();
//database.executeFast("DELETE FROM secret_holes WHERE uid = " + high_id).stepThis().dispose();
}
}
database.executeFast("UPDATE dialogs SET unread_count = 0 WHERE did = " + did).stepThis().dispose();
@ -1016,7 +1053,7 @@ public class MessagesStorage {
info.version = version;
final TLRPC.ChatParticipants finalInfo = info;
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.chatInfoDidLoaded, finalInfo.chat_id, finalInfo);
@ -1145,131 +1182,6 @@ public class MessagesStorage {
});
}
public void searchDialogs(final Integer token, final String query, final boolean needEncrypted) {
storageQueue.postRunnable(new Runnable() {
@Override
public void run() {
try {
ArrayList<TLRPC.User> encUsers = new ArrayList<TLRPC.User>();
String q = query.trim().toLowerCase();
if (q.length() == 0) {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.reloadSearchResults, token, new ArrayList<TLObject>(), new ArrayList<CharSequence>(), new ArrayList<CharSequence>());
return;
}
ArrayList<TLObject> resultArray = new ArrayList<TLObject>();
ArrayList<CharSequence> resultArrayNames = new ArrayList<CharSequence>();
SQLiteCursor cursor = database.queryFinalized("SELECT u.data, u.status, u.name FROM users as u INNER JOIN contacts as c ON u.uid = c.uid");
while (cursor.next()) {
String name = cursor.stringValue(2);
String username = null;
int usernamePos = name.lastIndexOf(";;;");
if (usernamePos != -1) {
username = name.substring(usernamePos + 3);
}
int found = 0;
if (name.startsWith(q) || name.contains(" " + q)) {
found = 1;
} else if (username != null && username.startsWith(q)) {
found = 2;
}
if (found != 0) {
ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) {
TLRPC.User user = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data, data.readInt32());
if (user.id != UserConfig.getClientUserId()) {
if (user.status != null) {
user.status.expires = cursor.intValue(1);
}
if (found == 1) {
resultArrayNames.add(Utilities.generateSearchName(user.first_name, user.last_name, q));
} else {
resultArrayNames.add(Utilities.generateSearchName("@" + user.username, null, "@" + q));
}
resultArray.add(user);
}
}
buffersStorage.reuseFreeBuffer(data);
}
}
cursor.dispose();
if (needEncrypted) {
cursor = database.queryFinalized("SELECT q.data, u.name, q.user, q.g, q.authkey, q.ttl, u.data, u.status, q.layer, q.seq_in, q.seq_out FROM enc_chats as q INNER JOIN dialogs as d ON (q.uid << 32) = d.did INNER JOIN users as u ON q.user = u.uid");
while (cursor.next()) {
String name = cursor.stringValue(1);
String username = null;
int usernamePos = name.lastIndexOf(";;;");
if (usernamePos != -1) {
username = name.substring(usernamePos + 2);
}
int found = 0;
if (name.startsWith(q) || name.contains(" " + q)) {
found = 1;
} else if (username != null && username.startsWith(q)) {
found = 2;
}
if (found != 0) {
ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0));
ByteBufferDesc data2 = buffersStorage.getFreeBuffer(cursor.byteArrayLength(6));
if (data != null && cursor.byteBufferValue(0, data.buffer) != 0 && cursor.byteBufferValue(6, data2.buffer) != 0) {
TLRPC.EncryptedChat chat = (TLRPC.EncryptedChat) TLClassStore.Instance().TLdeserialize(data, data.readInt32());
chat.user_id = cursor.intValue(2);
chat.a_or_b = cursor.byteArrayValue(3);
chat.auth_key = cursor.byteArrayValue(4);
chat.ttl = cursor.intValue(5);
chat.layer = cursor.intValue(8);
chat.seq_in = cursor.intValue(9);
chat.seq_out = cursor.intValue(10);
TLRPC.User user = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data2, data2.readInt32());
if (user.status != null) {
user.status.expires = cursor.intValue(7);
}
if (found == 1) {
resultArrayNames.add(Html.fromHtml("<font color=\"#00a60e\">" + ContactsController.formatName(user.first_name, user.last_name) + "</font>"));
} else {
resultArrayNames.add(Utilities.generateSearchName("@" + user.username, null, "@" + q));
}
resultArray.add(chat);
encUsers.add(user);
}
buffersStorage.reuseFreeBuffer(data);
buffersStorage.reuseFreeBuffer(data2);
}
}
cursor.dispose();
}
cursor = database.queryFinalized("SELECT data, name FROM chats");
while (cursor.next()) {
String name = cursor.stringValue(1);
String[] args = name.split(" ");
if (name.startsWith(q) || name.contains(" " + q)) {
ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) {
TLRPC.Chat chat = (TLRPC.Chat) TLClassStore.Instance().TLdeserialize(data, data.readInt32());
if (!needEncrypted && chat.id < 0) {
continue;
}
resultArrayNames.add(Utilities.generateSearchName(chat.title, null, q));
resultArray.add(chat);
}
buffersStorage.reuseFreeBuffer(data);
}
}
cursor.dispose();
NotificationCenter.getInstance().postNotificationName(NotificationCenter.reloadSearchResults, token, resultArray, resultArrayNames, encUsers);
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
});
}
public void putContacts(final ArrayList<TLRPC.TL_contact> contacts, final boolean deleteAll) {
if (contacts.isEmpty()) {
return;
@ -1427,9 +1339,6 @@ public class MessagesStorage {
StringBuilder uids = new StringBuilder();
while (cursor.next()) {
int user_id = cursor.intValue(0);
if (user_id == UserConfig.getClientUserId()) {
continue;
}
TLRPC.TL_contact contact = new TLRPC.TL_contact();
contact.user_id = user_id;
contact.mutual = cursor.intValue(1) == 1;
@ -1713,7 +1622,30 @@ public class MessagesStorage {
});
}
public void getMessages(final long dialog_id, final int count, final int max_id, final int minDate, final int classGuid, final boolean from_unread, final boolean forward, final Semaphore semaphore) {
/*private ArrayList<Range<Integer>> getHoles(long dialog_id) {
int lower_id = (int)dialog_id;
int high_id = (int)(dialog_id >> 32);
if (lower_id == 0 || lower_id != 0 && high_id == 1) {
return null;
}
ArrayList<Range<Integer>> holes = null;
try {
SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT start, end FROM messages_holes WHERE uid = %d", dialog_id));
while (cursor.next()) {
if (holes == null) {
holes = new ArrayList<Range<Integer>>();
}
holes.add(new Range<Integer>(cursor.intValue(0), cursor.intValue(1)));
}
cursor.dispose();
} catch (Exception e) {
FileLog.e("tmessages" , e);
}
return holes;
}*/
public void getMessages(final long dialog_id, final int count, final int max_id, final int minDate, final int classGuid, final int load_type) {
storageQueue.postRunnable(new Runnable() {
@Override
public void run() {
@ -1722,17 +1654,41 @@ public class MessagesStorage {
int count_query = count;
int offset_query = 0;
int min_unread_id = 0;
int max_unread_id = 0;
int last_message_id = 0;
int first_message_id = 0;
int max_unread_date = 0;
int hole_start = Integer.MAX_VALUE;
int hole_end = Integer.MAX_VALUE;
try {
ArrayList<Integer> loadedUsers = new ArrayList<Integer>();
ArrayList<Integer> fromUser = new ArrayList<Integer>();
SQLiteCursor cursor;
SQLiteCursor cursor = null;
int lower_id = (int)dialog_id;
if (lower_id != 0) {
if (forward) {
if (load_type == 3) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT max(mid), min(mid) FROM messages WHERE uid = %d AND mid > 0", dialog_id));
if (cursor.next()) {
last_message_id = cursor.intValue(0);
first_message_id = cursor.intValue(1);
}
cursor.dispose();
boolean containMessage = false;
cursor = database.queryFinalized(String.format(Locale.US, "SELECT mid FROM messages WHERE mid = %d", max_id));
if (cursor.next()) {
containMessage = true;
}
cursor.dispose();
if (containMessage) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT * FROM (SELECT read_state, data, send_state, mid, date FROM messages WHERE uid = %d AND mid <= %d ORDER BY date DESC, mid DESC LIMIT %d) UNION " +
"SELECT * FROM (SELECT read_state, data, send_state, mid, date FROM messages WHERE uid = %d AND mid > %d ORDER BY date ASC, mid ASC LIMIT %d)", dialog_id, max_id, count_query / 2, dialog_id, max_id, count_query / 2 - 1));
} else {
cursor = null;
}
} else if (load_type == 1) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT read_state, data, send_state, mid, date FROM messages WHERE uid = %d AND date >= %d AND mid > %d ORDER BY date ASC, mid ASC LIMIT %d", dialog_id, minDate, max_id, count_query));
} else if (minDate != 0) {
if (max_id != 0) {
@ -1741,12 +1697,17 @@ public class MessagesStorage {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT read_state, data, send_state, mid, date FROM messages WHERE uid = %d AND date <= %d ORDER BY date DESC, mid DESC LIMIT %d,%d", dialog_id, minDate, offset_query, count_query));
}
} else {
if (from_unread) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT min(mid), max(mid), max(date) FROM messages WHERE uid = %d AND out = 0 AND read_state = 0 AND mid > 0", dialog_id));
if (load_type == 2) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT max(mid) FROM messages WHERE uid = %d AND mid > 0", dialog_id));
if (cursor.next()) {
last_message_id = cursor.intValue(0);
}
cursor.dispose();
cursor = database.queryFinalized(String.format(Locale.US, "SELECT min(mid), max(date) FROM messages WHERE uid = %d AND out = 0 AND read_state = 0 AND mid > 0", dialog_id));
if (cursor.next()) {
min_unread_id = cursor.intValue(0);
max_unread_id = cursor.intValue(1);
max_unread_date = cursor.intValue(2);
max_unread_date = cursor.intValue(1);
}
cursor.dispose();
if (min_unread_id != 0) {
@ -1763,7 +1724,7 @@ public class MessagesStorage {
if (count_unread < 4) {
count_unread = 0;
min_unread_id = 0;
max_unread_id = 0;
last_message_id = 0;
}
} else {
offset_query = count_unread - count_query;
@ -1772,7 +1733,7 @@ public class MessagesStorage {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT read_state, data, send_state, mid, date FROM messages WHERE uid = %d ORDER BY date DESC, mid DESC LIMIT %d,%d", dialog_id, offset_query, count_query));
}
} else {
if (forward) {
if (load_type == 1) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT m.read_state, m.data, m.send_state, m.mid, m.date, r.random_id FROM messages as m LEFT JOIN randoms as r ON r.mid = m.mid WHERE m.uid = %d AND m.mid < %d ORDER BY m.mid DESC LIMIT %d", dialog_id, max_id, count_query));
} else if (minDate != 0) {
if (max_id != 0) {
@ -1781,12 +1742,17 @@ public class MessagesStorage {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT m.read_state, m.data, m.send_state, m.mid, m.date, r.random_id FROM messages as m LEFT JOIN randoms as r ON r.mid = m.mid WHERE m.uid = %d AND m.date <= %d ORDER BY m.mid ASC LIMIT %d,%d", dialog_id, minDate, offset_query, count_query));
}
} else {
if (from_unread) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT max(mid), min(mid), max(date) FROM messages WHERE uid = %d AND out = 0 AND read_state = 0 AND mid < 0", dialog_id));
if (load_type == 2) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT min(mid) FROM messages WHERE uid = %d AND mid < 0", dialog_id));
if (cursor.next()) {
last_message_id = cursor.intValue(0);
}
cursor.dispose();
cursor = database.queryFinalized(String.format(Locale.US, "SELECT max(mid), max(date) FROM messages WHERE uid = %d AND out = 0 AND read_state = 0 AND mid < 0", dialog_id));
if (cursor.next()) {
min_unread_id = cursor.intValue(0);
max_unread_id = cursor.intValue(1);
max_unread_date = cursor.intValue(2);
max_unread_date = cursor.intValue(1);
}
cursor.dispose();
if (min_unread_id != 0) {
@ -1803,7 +1769,7 @@ public class MessagesStorage {
if (count_unread < 4) {
count_unread = 0;
min_unread_id = 0;
max_unread_id = 0;
last_message_id = 0;
}
} else {
offset_query = count_unread - count_query;
@ -1812,6 +1778,7 @@ public class MessagesStorage {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT m.read_state, m.data, m.send_state, m.mid, m.date, r.random_id FROM messages as m LEFT JOIN randoms as r ON r.mid = m.mid WHERE m.uid = %d ORDER BY m.mid ASC LIMIT %d,%d", dialog_id, offset_query, count_query));
}
}
if (cursor != null) {
while (cursor.next()) {
ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(1));
if (data != null && cursor.byteBufferValue(1, data.buffer) != 0) {
@ -1854,58 +1821,63 @@ public class MessagesStorage {
}
}
buffersStorage.reuseFreeBuffer(data);
}
cursor.dispose();
}
Collections.sort(res.messages, new Comparator<TLRPC.Message>() {
@Override
public int compare(TLRPC.Message lhs, TLRPC.Message rhs) {
if (lhs.id > 0 && rhs.id > 0) {
if (!forward) {
if (lhs.id > rhs.id) {
return -1;
} else if (lhs.id < rhs.id) {
return 1;
}
} else {
if (lhs.id < rhs.id) {
return -1;
} else if (lhs.id > rhs.id) {
return 1;
}
}
} else if (lhs.id < 0 && rhs.id < 0) {
if (!forward) {
if (lhs.id < rhs.id) {
return -1;
} else if (lhs.id > rhs.id) {
return 1;
}
} else {
if (lhs.id > rhs.id) {
return -1;
} else if (lhs.id < rhs.id) {
return 1;
}
}
} else {
if (!forward) {
if (lhs.date > rhs.date) {
return -1;
} else if (lhs.date < rhs.date) {
return 1;
}
} else {
if (lhs.date < rhs.date) {
return -1;
} else if (lhs.date > rhs.date) {
return 1;
}
}
}
return 0;
}
});
/*ArrayList<Range<Integer>> holes = getHoles(dialog_id);
if (holes != null && !res.messages.isEmpty()) {
int start = res.messages.get(res.messages.size() - 1).id;
int end = res.messages.get(0).id;
for (Range<Integer> range : holes) {
if (range.contains(start) && range.contains(end)) {
res.messages.clear();
} else if (range.contains(start)) {
while (!res.messages.isEmpty() && range.contains(res.messages.get(res.messages.size() - 1).id)) {
res.messages.remove(res.messages.size() - 1);
}
cursor.dispose();
if (!res.messages.isEmpty()) {
start = res.messages.get(res.messages.size() - 1).id;
}
} else if (range.contains(end)) {
while (!res.messages.isEmpty() && range.contains(res.messages.get(0).id)) {
res.messages.remove(0);
}
if (!res.messages.isEmpty()) {
end = res.messages.get(0).id;
}
} else if (start >= )
if (res.messages.isEmpty()) {
break;
}
}
}*/
StringBuilder usersToLoad = new StringBuilder();
for (int uid : fromUser) {
@ -1926,7 +1898,7 @@ public class MessagesStorage {
res.users.clear();
FileLog.e("tmessages", e);
} finally {
MessagesController.getInstance().processLoadedMessages(res, dialog_id, count_query, max_id, true, classGuid, min_unread_id, max_unread_id, count_unread, max_unread_date, forward, semaphore);
MessagesController.getInstance().processLoadedMessages(res, dialog_id, count_query, max_id, true, classGuid, min_unread_id, last_message_id, first_message_id, count_unread, max_unread_date, load_type, false);
}
}
});
@ -2043,10 +2015,11 @@ public class MessagesStorage {
public void run() {
SQLitePreparedStatement state = null;
try {
state = database.executeFast("UPDATE enc_chats SET seq_in = ?, seq_out = ? WHERE uid = ?");
state = database.executeFast("UPDATE enc_chats SET seq_in = ?, seq_out = ?, use_count = ? WHERE uid = ?");
state.bindInteger(1, chat.seq_in);
state.bindInteger(2, chat.seq_out);
state.bindInteger(3, chat.id);
state.bindInteger(3, (int)chat.key_use_count_in << 16 | chat.key_use_count_out);
state.bindInteger(4, chat.id);
state.step();
} catch (Exception e) {
FileLog.e("tmessages", e);
@ -2116,10 +2089,18 @@ public class MessagesStorage {
public void run() {
SQLitePreparedStatement state = null;
try {
state = database.executeFast("UPDATE enc_chats SET data = ?, g = ?, authkey = ?, ttl = ?, layer = ?, seq_in = ?, seq_out = ? WHERE uid = ?");
if ((chat.key_hash == null || chat.key_hash.length != 16) && chat.auth_key != null) {
byte[] sha1 = Utilities.computeSHA1(chat.auth_key);
chat.key_hash = new byte[16];
System.arraycopy(sha1, 0, chat.key_hash, 0, chat.key_hash.length);
}
state = database.executeFast("UPDATE enc_chats SET data = ?, g = ?, authkey = ?, ttl = ?, layer = ?, seq_in = ?, seq_out = ?, use_count = ?, exchange_id = ?, key_date = ?, fprint = ?, fauthkey = ?, khash = ? WHERE uid = ?");
ByteBufferDesc data = buffersStorage.getFreeBuffer(chat.getObjectSize());
ByteBufferDesc data2 = buffersStorage.getFreeBuffer(chat.a_or_b != null ? chat.a_or_b.length : 1);
ByteBufferDesc data3 = buffersStorage.getFreeBuffer(chat.auth_key != null ? chat.auth_key.length : 1);
ByteBufferDesc data4 = buffersStorage.getFreeBuffer(chat.future_auth_key != null ? chat.future_auth_key.length : 1);
ByteBufferDesc data5 = buffersStorage.getFreeBuffer(chat.key_hash != null ? chat.key_hash.length : 1);
chat.serializeToStream(data);
state.bindByteBuffer(1, data.buffer);
if (chat.a_or_b != null) {
@ -2128,17 +2109,32 @@ public class MessagesStorage {
if (chat.auth_key != null) {
data3.writeRaw(chat.auth_key);
}
if (chat.future_auth_key != null) {
data4.writeRaw(chat.future_auth_key);
}
if (chat.key_hash != null) {
data5.writeRaw(chat.key_hash);
}
state.bindByteBuffer(2, data2.buffer);
state.bindByteBuffer(3, data3.buffer);
state.bindInteger(4, chat.ttl);
state.bindInteger(5, chat.layer);
state.bindInteger(6, chat.seq_in);
state.bindInteger(7, chat.seq_out);
state.bindInteger(8, chat.id);
state.bindInteger(8, (int)chat.key_use_count_in << 16 | chat.key_use_count_out);
state.bindLong(9, chat.exchange_id);
state.bindInteger(10, chat.key_create_date);
state.bindLong(11, chat.future_key_fingerprint);
state.bindByteBuffer(12, data4.buffer);
state.bindByteBuffer(13, data5.buffer);
state.bindInteger(14, chat.id);
state.step();
buffersStorage.reuseFreeBuffer(data);
buffersStorage.reuseFreeBuffer(data2);
buffersStorage.reuseFreeBuffer(data3);
buffersStorage.reuseFreeBuffer(data4);
buffersStorage.reuseFreeBuffer(data5);
} catch (Exception e) {
FileLog.e("tmessages", e);
} finally {
@ -2186,10 +2182,17 @@ public class MessagesStorage {
@Override
public void run() {
try {
SQLitePreparedStatement state = database.executeFast("REPLACE INTO enc_chats VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
if ((chat.key_hash == null || chat.key_hash.length != 16) && chat.auth_key != null) {
byte[] sha1 = Utilities.computeSHA1(chat.auth_key);
chat.key_hash = new byte[16];
System.arraycopy(sha1, 0, chat.key_hash, 0, chat.key_hash.length);
}
SQLitePreparedStatement state = database.executeFast("REPLACE INTO enc_chats VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
ByteBufferDesc data = buffersStorage.getFreeBuffer(chat.getObjectSize());
ByteBufferDesc data2 = buffersStorage.getFreeBuffer(chat.a_or_b != null ? chat.a_or_b.length : 1);
ByteBufferDesc data3 = buffersStorage.getFreeBuffer(chat.auth_key != null ? chat.auth_key.length : 1);
ByteBufferDesc data4 = buffersStorage.getFreeBuffer(chat.future_auth_key != null ? chat.future_auth_key.length : 1);
ByteBufferDesc data5 = buffersStorage.getFreeBuffer(chat.key_hash != null ? chat.key_hash.length : 1);
chat.serializeToStream(data);
state.bindInteger(1, chat.id);
@ -2202,17 +2205,32 @@ public class MessagesStorage {
if (chat.auth_key != null) {
data3.writeRaw(chat.auth_key);
}
if (chat.future_auth_key != null) {
data4.writeRaw(chat.future_auth_key);
}
if (chat.key_hash != null) {
data5.writeRaw(chat.key_hash);
}
state.bindByteBuffer(5, data2.buffer);
state.bindByteBuffer(6, data3.buffer);
state.bindInteger(7, chat.ttl);
state.bindInteger(8, chat.layer);
state.bindInteger(9, chat.seq_in);
state.bindInteger(10, chat.seq_out);
state.bindInteger(11, (int)chat.key_use_count_in << 16 | chat.key_use_count_out);
state.bindLong(12, chat.exchange_id);
state.bindInteger(13, chat.key_create_date);
state.bindLong(14, chat.future_key_fingerprint);
state.bindByteBuffer(15, data4.buffer);
state.bindByteBuffer(16, data5.buffer);
state.step();
state.dispose();
buffersStorage.reuseFreeBuffer(data);
buffersStorage.reuseFreeBuffer(data2);
buffersStorage.reuseFreeBuffer(data3);
buffersStorage.reuseFreeBuffer(data4);
buffersStorage.reuseFreeBuffer(data5);
if (dialog != null) {
state = database.executeFast("REPLACE INTO dialogs(did, date, unread_count, last_mid) VALUES(?, ?, ?, ?)");
@ -2260,6 +2278,13 @@ public class MessagesStorage {
state.bindInteger(1, user.id);
state.bindString(2, formatUserSearchName(user));
if (user.status != null) {
if (user.status instanceof TLRPC.TL_userStatusRecently) {
user.status.expires = -100;
} else if (user.status instanceof TLRPC.TL_userStatusLastWeek) {
user.status.expires = -101;
} else if (user.status instanceof TLRPC.TL_userStatusLastMonth) {
user.status.expires = -102;
}
state.bindInteger(3, user.status.expires);
} else {
state.bindInteger(3, 0);
@ -2345,8 +2370,8 @@ public class MessagesStorage {
if (chatsToLoad == null || chatsToLoad.length() == 0 || result == null) {
return;
}
SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, user, g, authkey, ttl, layer, seq_in, seq_out FROM enc_chats WHERE uid IN(%s)", chatsToLoad));
//use_count INTEGER, exchange_id INTEGER, key_date INTEGER, fprint INTEGER, fauthkey BLOB
SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, user, g, authkey, ttl, layer, seq_in, seq_out, use_count, exchange_id, key_date, fprint, fauthkey, khash FROM enc_chats WHERE uid IN(%s)", chatsToLoad));
while (cursor.next()) {
try {
ByteBufferDesc data = buffersStorage.getFreeBuffer(cursor.byteArrayLength(0));
@ -2363,6 +2388,14 @@ public class MessagesStorage {
chat.layer = cursor.intValue(5);
chat.seq_in = cursor.intValue(6);
chat.seq_out = cursor.intValue(7);
int use_count = cursor.intValue(8);
chat.key_use_count_in = (short)(use_count >> 16);
chat.key_use_count_out = (short)(use_count);
chat.exchange_id = cursor.longValue(9);
chat.key_create_date = cursor.intValue(10);
chat.future_key_fingerprint = cursor.longValue(11);
chat.future_auth_key = cursor.byteArrayValue(12);
chat.key_hash = cursor.byteArrayValue(13);
result.add(chat);
}
}
@ -2467,7 +2500,7 @@ public class MessagesStorage {
}
cursor.dispose();
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
MediaController.getInstance().processDownloadObjects(type, objects);
@ -2757,7 +2790,7 @@ public class MessagesStorage {
if (downloadMediaMask != 0) {
final int downloadMediaMaskFinal = downloadMediaMask;
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
MediaController.getInstance().newDownloadObjectsAvailable(downloadMediaMaskFinal);
@ -2798,6 +2831,60 @@ public class MessagesStorage {
});
}
/*public void getHoleMessages() {
storageQueue.postRunnable(new Runnable() {
@Override
public void run() {
try {
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
});
}
public void clearHoleMessages(final int enc_id) {
storageQueue.postRunnable(new Runnable() {
@Override
public void run() {
try {
database.executeFast("DELETE FROM secret_holes WHERE uid = " + enc_id).stepThis().dispose();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
});
}
public void putHoleMessage(final int enc_id, final TLRPC.Message message) {
if (message == null) {
return;
}
storageQueue.postRunnable(new Runnable() {
@Override
public void run() {
try {
SQLitePreparedStatement state = database.executeFast("REPLACE INTO secret_holes VALUES(?, ?, ?, ?)");
state.requery();
ByteBufferDesc data = buffersStorage.getFreeBuffer(message.getObjectSize());
message.serializeToStream(data);
state.bindInteger(1, enc_id);
state.bindInteger(2, message.seq_in);
state.bindInteger(3, message.seq_out);
state.bindByteBuffer(4, data.buffer);
state.step();
buffersStorage.reuseFreeBuffer(data);
state.dispose();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
});
}*/
public void setMessageSeq(final int mid, final int seq_in, final int seq_out) {
storageQueue.postRunnable(new Runnable() {
@Override
@ -2968,6 +3055,8 @@ public class MessagesStorage {
user.username = updateUser.username;
} else if (updateUser.photo != null) {
user.photo = updateUser.photo;
} else if (updateUser.phone != null) {
user.phone = updateUser.phone;
}
}
}
@ -3058,7 +3147,7 @@ public class MessagesStorage {
}
cursor.dispose();
if (!mids.isEmpty()) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
for (Integer id : mids) {

View File

@ -23,12 +23,10 @@ import java.util.zip.ZipFile;
public class NativeLoader {
private static final long sizes[] = new long[] {
955148, //armeabi
1041184, //armeabi-v7a
1616116, //x86
0, //mips
};
private final static int LIB_VERSION = 4;
private final static String LIB_NAME = "tmessages." + LIB_VERSION;
private final static String LIB_SO_NAME = "lib" + LIB_NAME + ".so";
private final static String LOCALE_LIB_SO_NAME = "lib" + LIB_NAME + "loc.so";
private static volatile boolean nativeLoaded = false;
@ -50,14 +48,22 @@ public class NativeLoader {
return null;
}
private static boolean loadFromZip(Context context, File destLocalFile, String folder) {
private static boolean loadFromZip(Context context, File destDir, File destLocalFile, String folder) {
try {
for (File file : destDir.listFiles()) {
file.delete();
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
ZipFile zipFile = null;
InputStream stream = null;
try {
zipFile = new ZipFile(context.getApplicationInfo().sourceDir);
ZipEntry entry = zipFile.getEntry("lib/" + folder + "/libtmessages.so");
ZipEntry entry = zipFile.getEntry("lib/" + folder + "/" + LIB_SO_NAME);
if (entry == null) {
throw new Exception("Unable to find file in apk:" + "lib/" + folder + "/libtmessages.so");
throw new Exception("Unable to find file in apk:" + "lib/" + folder + "/" + LIB_NAME);
}
stream = zipFile.getInputStream(entry);
@ -70,6 +76,12 @@ public class NativeLoader {
}
out.close();
if (Build.VERSION.SDK_INT >= 9) {
destLocalFile.setReadable(true, false);
destLocalFile.setExecutable(true, false);
destLocalFile.setWritable(true);
}
try {
System.load(destLocalFile.getAbsolutePath());
nativeLoaded = true;
@ -105,50 +117,38 @@ public class NativeLoader {
try {
String folder = null;
long libSize = 0;
long libSize2 = 0;
try {
if (Build.CPU_ABI.equalsIgnoreCase("armeabi-v7a")) {
folder = "armeabi-v7a";
libSize = sizes[1];
libSize2 = sizes[0];
} else if (Build.CPU_ABI.equalsIgnoreCase("armeabi")) {
folder = "armeabi";
libSize = sizes[0];
libSize2 = sizes[1];
} else if (Build.CPU_ABI.equalsIgnoreCase("x86")) {
folder = "x86";
libSize = sizes[2];
} else if (Build.CPU_ABI.equalsIgnoreCase("mips")) {
folder = "mips";
libSize = sizes[3];
} else {
folder = "armeabi";
libSize = sizes[0];
libSize2 = sizes[1];
FileLog.e("tmessages", "Unsupported arch: " + Build.CPU_ABI);
}
} catch (Exception e) {
FileLog.e("tmessages", e);
folder = "armeabi";
libSize = sizes[0];
libSize2 = sizes[1];
}
String javaArch = System.getProperty("os.arch");
if (javaArch != null && javaArch.contains("686")) {
folder = "x86";
libSize = sizes[2];
}
File destFile = getNativeLibraryDir(context);
if (destFile != null) {
destFile = new File(destFile, "libtmessages.so");
if (destFile.exists() && (destFile.length() == libSize || libSize2 != 0 && destFile.length() == libSize2)) {
destFile = new File(destFile, LIB_SO_NAME);
if (destFile.exists()) {
FileLog.d("tmessages", "Load normal lib");
try {
System.loadLibrary("tmessages");
System.loadLibrary(LIB_NAME);
nativeLoaded = true;
return;
} catch (Error e) {
@ -157,9 +157,11 @@ public class NativeLoader {
}
}
File destLocalFile = new File(context.getFilesDir().getAbsolutePath() + "/libtmessages.so");
File destDir = new File(context.getFilesDir(), "lib");
destDir.mkdirs();
File destLocalFile = new File(destDir, LOCALE_LIB_SO_NAME);
if (destLocalFile != null && destLocalFile.exists()) {
if (destLocalFile.length() == libSize) {
try {
FileLog.d("tmessages", "Load local lib");
System.load(destLocalFile.getAbsolutePath());
@ -168,14 +170,16 @@ public class NativeLoader {
} catch (Error e) {
FileLog.e("tmessages", e);
}
} else {
destLocalFile.delete();
}
}
FileLog.e("tmessages", "Library not found, arch = " + folder);
if (!loadFromZip(context, destLocalFile, folder)) {
if (loadFromZip(context, destDir, destLocalFile, folder)) {
return;
}
/*
folder = "x86";
destLocalFile = new File(context.getFilesDir().getAbsolutePath() + "/libtmessages86.so");
if (!loadFromZip(context, destLocalFile, folder)) {
@ -183,13 +187,13 @@ public class NativeLoader {
folder = "armeabi";
loadFromZip(context, destLocalFile, folder);
}
}
*/
} catch (Throwable e) {
e.printStackTrace();
}
try {
System.loadLibrary("tmessages");
System.loadLibrary(LIB_NAME);
nativeLoaded = true;
} catch (Error e) {
FileLog.e("tmessages", e);

View File

@ -23,7 +23,6 @@ public class NotificationCenter {
public static final int messageReceivedByAck = 9;
public static final int messageReceivedByServer = 10;
public static final int messageSendError = 11;
public static final int reloadSearchResults = 12;
public static final int contactsDidLoaded = 13;
public static final int chatDidCreated = 15;
public static final int chatDidFailCreate = 16;
@ -42,10 +41,13 @@ public class NotificationCenter {
public static final int hideEmojiKeyboard = 30;
public static final int stopEncodingService = 31;
public static final int didCreatedNewDeleteTask = 32;
public static final int mainUserInfoChanged = 33;
public static final int privacyRulesUpdated = 34;
public static final int wallpapersDidLoaded = 171;
public static final int closeOtherAppActivities = 702;
public static final int didUpdatedConnectionState = 703;
public static final int didReceiveSmsCode = 998;
public static final int emojiDidLoaded = 999;
public static final int appDidLogout = 1234;

View File

@ -21,7 +21,7 @@ public class NotificationRepeat extends IntentService {
@Override
protected void onHandleIntent(Intent intent) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationsController.getInstance().repeatNotificationMaybe();

View File

@ -8,6 +8,7 @@
package org.telegram.android;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
@ -32,7 +33,7 @@ import org.telegram.messenger.FileLog;
import org.telegram.messenger.R;
import org.telegram.messenger.TLRPC;
import org.telegram.messenger.UserConfig;
import org.telegram.ui.ApplicationLoader;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.ui.LaunchActivity;
import org.telegram.ui.PopupNotificationActivity;
@ -235,8 +236,10 @@ public class NotificationsController {
try {
AlarmManager alarm = (AlarmManager) ApplicationLoader.applicationContext.getSystemService(Context.ALARM_SERVICE);
PendingIntent pintent = PendingIntent.getService(ApplicationLoader.applicationContext, 0, new Intent(ApplicationLoader.applicationContext, NotificationRepeat.class), 0);
if (personal_count > 0) {
alarm.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 60 * 60 * 1000, pintent);
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE);
int minutes = preferences.getInt("repeat_messages", 60);
if (minutes > 0 && personal_count > 0) {
alarm.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + minutes * 60 * 1000, pintent);
} else {
alarm.cancel(pintent);
}
@ -289,6 +292,9 @@ public class NotificationsController {
boolean inAppSounds = false;
boolean inAppVibrate = false;
boolean inAppPreview = false;
boolean inAppPriority = false;
int priority = 0;
int priority_override = 0;
int vibrate_override = 0;
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Context.MODE_PRIVATE);
@ -302,7 +308,9 @@ public class NotificationsController {
inAppSounds = preferences.getBoolean("EnableInAppSounds", true);
inAppVibrate = preferences.getBoolean("EnableInAppVibrate", true);
inAppPreview = preferences.getBoolean("EnableInAppPreview", true);
inAppPriority = preferences.getBoolean("EnableInAppPriority", false);
vibrate_override = preferences.getInt("vibrate_" + dialog_id, 0);
priority_override = preferences.getInt("priority_" + dialog_id, 3);
choosenSoundPath = preferences.getString("sound_path_" + dialog_id, null);
if (chat_id != 0) {
@ -312,6 +320,7 @@ public class NotificationsController {
choosenSoundPath = preferences.getString("GroupSoundPath", defaultPath);
}
needVibrate = preferences.getInt("vibrate_group", 0);
priority = preferences.getInt("priority_group", 1);
ledColor = preferences.getInt("GroupLed", 0xff00ff00);
} else if (user_id != 0) {
if (choosenSoundPath != null && choosenSoundPath.equals(defaultPath)) {
@ -320,12 +329,17 @@ public class NotificationsController {
choosenSoundPath = preferences.getString("GlobalSoundPath", defaultPath);
}
needVibrate = preferences.getInt("vibrate_messages", 0);
priority = preferences.getInt("priority_group", 1);
ledColor = preferences.getInt("MessagesLed", 0xff00ff00);
}
if (preferences.contains("color_" + dialog_id)) {
ledColor = preferences.getInt("color_" + dialog_id, 0);
}
if (priority_override != 3) {
priority = priority_override;
}
if (needVibrate == 2 && (vibrate_override == 1 || vibrate_override == 3 || vibrate_override == 5) || needVibrate != 2 && vibrate_override == 2 || vibrate_override != 0) {
needVibrate = vibrate_override;
}
@ -336,6 +350,11 @@ public class NotificationsController {
if (!inAppVibrate) {
needVibrate = 2;
}
if (!inAppPriority) {
priority = 0;
} else if (priority == 2) {
priority = 1;
}
}
}
@ -397,6 +416,22 @@ public class NotificationsController {
.setGroup("messages")
.setGroupSummary(true);
if (priority == 0) {
mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
} else if (priority == 1) {
mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);
} else if (priority == 2) {
mBuilder.setPriority(NotificationCompat.PRIORITY_MAX);
}
mBuilder.setCategory(NotificationCompat.CATEGORY_MESSAGE);
if (chat == null && user != null && user.phone != null && user.phone.length() > 0) {
mBuilder.addPerson("tel:+" + user.phone);
}
/*Bundle bundle = new Bundle();
bundle.putString(NotificationCompat.EXTRA_PEOPLE, );
mBuilder.setExtras()*/
String lastMessage = null;
String lastMessageFull = null;
if (pushMessages.size() == 1) {
@ -600,7 +635,12 @@ public class NotificationsController {
.setContentText(text)
.setGroupSummary(false)
.setContentIntent(contentIntent)
.extend(new NotificationCompat.WearableExtender().addAction(action));
.extend(new NotificationCompat.WearableExtender().addAction(action))
.setCategory(NotificationCompat.CATEGORY_MESSAGE);
if (chat == null && user != null && user.phone != null && user.phone.length() > 0) {
builder.addPerson("tel:+" + user.phone);
}
notificationManager.notify(notificationId, builder.build());
wearNoticationsIds.put(dialog_id, notificationId);

View File

@ -14,7 +14,7 @@ import android.content.SharedPreferences;
import android.os.IBinder;
import org.telegram.messenger.FileLog;
import org.telegram.ui.ApplicationLoader;
import org.telegram.messenger.ApplicationLoader;
public class NotificationsService extends Service {

View File

@ -18,6 +18,7 @@ import org.telegram.messenger.Utilities;
import java.util.ArrayList;
public class PhotoObject {
public TLRPC.PhotoSize photoOwner;
public Bitmap image;

View File

@ -14,7 +14,7 @@ import android.content.Intent;
import org.telegram.messenger.ConnectionsManager;
import org.telegram.messenger.FileLog;
import org.telegram.ui.ApplicationLoader;
import org.telegram.messenger.ApplicationLoader;
public class ScreenReceiver extends BroadcastReceiver {
@Override

File diff suppressed because it is too large Load Diff

View File

@ -16,19 +16,18 @@ import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import android.webkit.MimeTypeMap;
import android.widget.Toast;
import org.telegram.messenger.BuffersStorage;
import org.telegram.messenger.ByteBufferDesc;
import org.telegram.messenger.ConnectionsManager;
import org.telegram.messenger.FileLoader;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.MessageKeyData;
import org.telegram.messenger.R;
import org.telegram.messenger.RPCRequest;
import org.telegram.messenger.TLObject;
import org.telegram.messenger.TLRPC;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.Utilities;
import org.telegram.ui.ApplicationLoader;
import org.telegram.messenger.ApplicationLoader;
import java.io.File;
import java.util.ArrayList;
@ -36,12 +35,9 @@ import java.util.HashMap;
public class SendMessagesHelper implements NotificationCenter.NotificationCenterDelegate {
public static final int CURRENT_SECRET_CHAT_LAYER = 17;
private TLRPC.ChatParticipants currentChatInfo = null;
private HashMap<String, ArrayList<DelayedMessage>> delayedMessages = new HashMap<String, ArrayList<DelayedMessage>>();
private HashMap<Integer, MessageObject> unsentMessages = new HashMap<Integer, MessageObject>();
private ArrayList<Integer> sendingNotifyLayer = new ArrayList<Integer>();
private class DelayedMessage {
public TLObject sendRequest;
@ -80,7 +76,6 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
public void cleanUp() {
delayedMessages.clear();
sendingNotifyLayer.clear();
unsentMessages.clear();
currentChatInfo = null;
}
@ -96,7 +91,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
final TLRPC.InputFile file = (TLRPC.InputFile)args[1];
final TLRPC.InputEncryptedFile encryptedFile = (TLRPC.InputEncryptedFile)args[2];
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
ArrayList<DelayedMessage> arr = delayedMessages.get(location);
@ -147,7 +142,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
} else if (encryptedFile != null && message.sendEncryptedRequest != null) {
message.sendEncryptedRequest.media.key = encryptedFile.key;
message.sendEncryptedRequest.media.iv = encryptedFile.iv;
performSendEncryptedRequest(message.sendEncryptedRequest, message.obj.messageOwner, message.encryptedChat, encryptedFile, message.originalPath);
SecretChatHelper.getInstance().performSendEncryptedRequest(message.sendEncryptedRequest, message.obj.messageOwner, message.encryptedChat, encryptedFile, message.originalPath);
arr.remove(a);
a--;
}
@ -162,7 +157,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
final String location = (String) args[0];
final boolean enc = (Boolean) args[1];
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
ArrayList<DelayedMessage> arr = delayedMessages.get(location);
@ -299,17 +294,17 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
return false;
}
if (messageObject.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL) {
sendTTLMessage(encryptedChat, messageObject.messageOwner);
SecretChatHelper.getInstance().sendTTLMessage(encryptedChat, messageObject.messageOwner);
} else if (messageObject.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionDeleteMessages) {
sendMessagesDeleteMessage(encryptedChat, null, messageObject.messageOwner);
SecretChatHelper.getInstance().sendMessagesDeleteMessage(encryptedChat, null, messageObject.messageOwner);
} else if (messageObject.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionFlushHistory) {
sendClearHistoryMessage(encryptedChat, messageObject.messageOwner);
SecretChatHelper.getInstance().sendClearHistoryMessage(encryptedChat, messageObject.messageOwner);
} else if (messageObject.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionNotifyLayer) {
sendNotifyLayerMessage(encryptedChat, messageObject.messageOwner);
SecretChatHelper.getInstance().sendNotifyLayerMessage(encryptedChat, messageObject.messageOwner);
} else if (messageObject.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionReadMessages) {
sendMessagesReadMessage(encryptedChat, null, messageObject.messageOwner);
SecretChatHelper.getInstance().sendMessagesReadMessage(encryptedChat, null, messageObject.messageOwner);
} else if (messageObject.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionScreenshotMessages) {
sendScreenshotMessage(encryptedChat, null, messageObject.messageOwner);
SecretChatHelper.getInstance().sendScreenshotMessage(encryptedChat, null, messageObject.messageOwner);
} else if (messageObject.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionTyping) {
} else if (messageObject.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionResend) {
@ -324,7 +319,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
return true;
}
public void processSentMessage(int id) {
protected void processSentMessage(int id) {
int prevSize = unsentMessages.size();
unsentMessages.remove(id);
if (prevSize != 0 && unsentMessages.size() == 0) {
@ -675,7 +670,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
reqSend.random_id = newMsg.random_id;
reqSend.message = message;
reqSend.media = new TLRPC.TL_decryptedMessageMediaEmpty();
performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, null, null);
SecretChatHelper.getInstance().performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, null, null);
}
} else if (type >= 1 && type <= 3 || type >= 5 && type <= 8) {
if (encryptedChat == null) {
@ -836,7 +831,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
reqSend.media = new TLRPC.TL_decryptedMessageMediaGeoPoint();
reqSend.media.lat = lat;
reqSend.media._long = lon;
performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, null, null);
SecretChatHelper.getInstance().performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, null, null);
} else if (type == 2) {
TLRPC.PhotoSize small = photo.sizes.get(0);
TLRPC.PhotoSize big = photo.sizes.get(photo.sizes.size() - 1);
@ -862,7 +857,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
encryptedFile.access_hash = big.location.secret;
reqSend.media.key = big.location.key;
reqSend.media.iv = big.location.iv;
performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, encryptedFile, null);
SecretChatHelper.getInstance().performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, encryptedFile, null);
}
} else if (type == 3) {
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
@ -893,7 +888,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
encryptedFile.access_hash = video.access_hash;
reqSend.media.key = video.key;
reqSend.media.iv = video.iv;
performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, encryptedFile, null);
SecretChatHelper.getInstance().performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, encryptedFile, null);
}
} else if (type == 6) {
reqSend.media = new TLRPC.TL_decryptedMessageMediaContact();
@ -901,7 +896,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
reqSend.media.first_name = user.first_name;
reqSend.media.last_name = user.last_name;
reqSend.media.user_id = user.id;
performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, null, null);
SecretChatHelper.getInstance().performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, null, null);
} else if (type == 7) {
reqSend.media = new TLRPC.TL_decryptedMessageMediaDocument();
reqSend.media.size = document.size;
@ -931,7 +926,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
encryptedFile.access_hash = document.access_hash;
reqSend.media.key = document.key;
reqSend.media.iv = document.iv;
performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, encryptedFile, null);
SecretChatHelper.getInstance().performSendEncryptedRequest(reqSend, newMsgObj.messageOwner, encryptedChat, encryptedFile, null);
}
} else if (type == 8) {
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
@ -1063,11 +1058,11 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
}
}
private void stopVideoService(final String path) {
MessagesStorage.getInstance().storageQueue.postRunnable(new Runnable() {
protected void stopVideoService(final String path) {
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.stopEncodingService, path);
@ -1096,7 +1091,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
TLRPC.messages_StatedMessage res = (TLRPC.messages_StatedMessage) response;
sentMessages.add(res.message);
newMsgObj.id = res.message.id;
processSentMessage(newMsgObj, res.message, null, null, originalPath);
processSentMessage(newMsgObj, res.message, originalPath);
MessagesController.getInstance().processNewDifferenceParams(res.seq, res.pts, res.message.date);
} else if (response instanceof TLRPC.messages_StatedMessages) {
TLRPC.messages_StatedMessages res = (TLRPC.messages_StatedMessages) response;
@ -1106,11 +1101,11 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
if (!isBroadcast) {
newMsgObj.id = message.id;
}
processSentMessage(newMsgObj, message, null, null, originalPath);
processSentMessage(newMsgObj, message, originalPath);
}
MessagesController.getInstance().processNewDifferenceParams(res.seq, res.pts, -1);
}
MessagesStorage.getInstance().storageQueue.postRunnable(new Runnable() {
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
MessagesStorage.getInstance().updateMessageStateAndId(newMsgObj.random_id, oldId, (isBroadcast ? oldId : newMsgObj.id), 0, false);
@ -1121,7 +1116,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SENT;
MessagesStorage.getInstance().putMessages(currentMessage, true, false, false, 0);
}
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SENT;
@ -1145,7 +1140,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
});
} else {
MessagesStorage.getInstance().markMessageAsSendError(newMsgObj.id);
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SEND_ERROR;
@ -1162,7 +1157,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
@Override
public void quickAck() {
final int msg_id = newMsgObj.id;
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SENT;
@ -1173,179 +1168,10 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
}), true, RPCRequest.RPCRequestClassGeneric | RPCRequest.RPCRequestClassCanCompress, ConnectionsManager.DEFAULT_DATACENTER_ID);
}
private void performSendEncryptedRequest(final TLRPC.DecryptedMessage req, final TLRPC.Message newMsgObj, final TLRPC.EncryptedChat chat, final TLRPC.InputEncryptedFile encryptedFile, final String originalPath) {
if (req == null || chat.auth_key == null || chat instanceof TLRPC.TL_encryptedChatRequested || chat instanceof TLRPC.TL_encryptedChatWaiting) {
private void processSentMessage(TLRPC.Message newMsg, TLRPC.Message sentMessage, String originalPath) {
if (sentMessage == null) {
return;
}
TLObject toEncryptObject = null;
if (AndroidUtilities.getPeerLayerVersion(chat.layer) >= 17) {
TLRPC.TL_decryptedMessageLayer layer = new TLRPC.TL_decryptedMessageLayer();
layer.layer = CURRENT_SECRET_CHAT_LAYER;
layer.message = req;
layer.random_bytes = new byte[Math.max(1, (int) Math.ceil(Utilities.random.nextDouble() * 16))];
Utilities.random.nextBytes(layer.random_bytes);
toEncryptObject = layer;
if (chat.seq_in == 0 && chat.seq_out == 0) {
if (chat.admin_id == UserConfig.getClientUserId()) {
chat.seq_out = 1;
} else {
chat.seq_in = 1;
}
}
if (newMsgObj.seq_in == 0 && newMsgObj.seq_out == 0) {
layer.in_seq_no = chat.seq_in;
layer.out_seq_no = chat.seq_out;
chat.seq_out += 2;
MessagesStorage.getInstance().updateEncryptedChatSeq(chat);
if (newMsgObj != null) {
newMsgObj.seq_in = layer.in_seq_no;
newMsgObj.seq_out = layer.out_seq_no;
MessagesStorage.getInstance().setMessageSeq(newMsgObj.id, newMsgObj.seq_in, newMsgObj.seq_out);
}
} else {
layer.in_seq_no = newMsgObj.seq_in;
layer.out_seq_no = newMsgObj.seq_out;
}
} else {
toEncryptObject = req;
}
int len = toEncryptObject.getObjectSize();
ByteBufferDesc toEncrypt = BuffersStorage.getInstance().getFreeBuffer(4 + len);
toEncrypt.writeInt32(len);
toEncryptObject.serializeToStream(toEncrypt);
byte[] messageKeyFull = Utilities.computeSHA1(toEncrypt.buffer);
byte[] messageKey = new byte[16];
System.arraycopy(messageKeyFull, messageKeyFull.length - 16, messageKey, 0, 16);
MessageKeyData keyData = Utilities.generateMessageKeyData(chat.auth_key, messageKey, false);
len = toEncrypt.length();
int extraLen = len % 16 != 0 ? 16 - len % 16 : 0;
ByteBufferDesc dataForEncryption = BuffersStorage.getInstance().getFreeBuffer(len + extraLen);
toEncrypt.position(0);
dataForEncryption.writeRaw(toEncrypt);
if (extraLen != 0) {
byte[] b = new byte[extraLen];
Utilities.random.nextBytes(b);
dataForEncryption.writeRaw(b);
}
BuffersStorage.getInstance().reuseFreeBuffer(toEncrypt);
Utilities.aesIgeEncryption(dataForEncryption.buffer, keyData.aesKey, keyData.aesIv, true, false, 0, dataForEncryption.limit());
ByteBufferDesc data = BuffersStorage.getInstance().getFreeBuffer(8 + messageKey.length + dataForEncryption.length());
dataForEncryption.position(0);
data.writeInt64(chat.key_fingerprint);
data.writeRaw(messageKey);
data.writeRaw(dataForEncryption);
BuffersStorage.getInstance().reuseFreeBuffer(dataForEncryption);
data.position(0);
TLObject reqToSend = null;
if (encryptedFile == null) {
if (req instanceof TLRPC.TL_decryptedMessageService) {
TLRPC.TL_messages_sendEncryptedService req2 = new TLRPC.TL_messages_sendEncryptedService();
req2.data = data;
req2.random_id = req.random_id;
req2.peer = new TLRPC.TL_inputEncryptedChat();
req2.peer.chat_id = chat.id;
req2.peer.access_hash = chat.access_hash;
reqToSend = req2;
} else {
TLRPC.TL_messages_sendEncrypted req2 = new TLRPC.TL_messages_sendEncrypted();
req2.data = data;
req2.random_id = req.random_id;
req2.peer = new TLRPC.TL_inputEncryptedChat();
req2.peer.chat_id = chat.id;
req2.peer.access_hash = chat.access_hash;
reqToSend = req2;
}
} else {
TLRPC.TL_messages_sendEncryptedFile req2 = new TLRPC.TL_messages_sendEncryptedFile();
req2.data = data;
req2.random_id = req.random_id;
req2.peer = new TLRPC.TL_inputEncryptedChat();
req2.peer.chat_id = chat.id;
req2.peer.access_hash = chat.access_hash;
req2.file = encryptedFile;
reqToSend = req2;
}
ConnectionsManager.getInstance().performRpc(reqToSend, new RPCRequest.RPCRequestDelegate() {
@Override
public void run(TLObject response, TLRPC.TL_error error) {
if (error == null) {
if (req.action instanceof TLRPC.TL_decryptedMessageActionNotifyLayer) {
AndroidUtilities.RunOnUIThread(new Runnable() {
@Override
public void run() {
TLRPC.EncryptedChat currentChat = MessagesController.getInstance().getEncryptedChat(chat.id);
sendingNotifyLayer.remove((Integer)currentChat.id);
currentChat.layer = AndroidUtilities.setMyLayerVersion(currentChat.layer, CURRENT_SECRET_CHAT_LAYER);
MessagesStorage.getInstance().updateEncryptedChatLayer(currentChat);
}
});
}
}
if (newMsgObj != null) {
if (error == null) {
final String attachPath = newMsgObj.attachPath;
final TLRPC.messages_SentEncryptedMessage res = (TLRPC.messages_SentEncryptedMessage) response;
if (newMsgObj.action instanceof TLRPC.TL_messageEncryptedAction) {
if (newMsgObj.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionScreenshotMessages || newMsgObj.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL) {
newMsgObj.date = res.date;
}
}
if (res.file instanceof TLRPC.TL_encryptedFile) {
processSentMessage(newMsgObj, null, res.file, req, originalPath);
}
MessagesStorage.getInstance().storageQueue.postRunnable(new Runnable() {
@Override
public void run() {
if (newMsgObj.action instanceof TLRPC.TL_messageEncryptedAction) {
if (!(newMsgObj.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionScreenshotMessages || newMsgObj.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL)) {
res.date = 0;
}
}
MessagesStorage.getInstance().updateMessageStateAndId(newMsgObj.random_id, newMsgObj.id, newMsgObj.id, res.date, false);
AndroidUtilities.RunOnUIThread(new Runnable() {
@Override
public void run() {
newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SENT;
NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageReceivedByServer, newMsgObj.id, newMsgObj.id, newMsgObj);
processSentMessage(newMsgObj.id);
if (newMsgObj.media instanceof TLRPC.TL_messageMediaVideo) {
stopVideoService(attachPath);
}
}
});
}
});
} else {
MessagesStorage.getInstance().markMessageAsSendError(newMsgObj.id);
AndroidUtilities.RunOnUIThread(new Runnable() {
@Override
public void run() {
newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SEND_ERROR;
NotificationCenter.getInstance().postNotificationName(NotificationCenter.messageSendError, newMsgObj.id);
processSentMessage(newMsgObj.id);
if (newMsgObj.media instanceof TLRPC.TL_messageMediaVideo) {
stopVideoService(newMsgObj.attachPath);
}
}
});
}
}
}
});
}
private void processSentMessage(TLRPC.Message newMsg, TLRPC.Message sentMessage, TLRPC.EncryptedFile file, TLRPC.DecryptedMessage decryptedMessage, String originalPath) {
if (sentMessage != null) {
if (sentMessage.media instanceof TLRPC.TL_messageMediaPhoto && sentMessage.media.photo != null && newMsg.media instanceof TLRPC.TL_messageMediaPhoto && newMsg.media.photo != null) {
MessagesStorage.getInstance().putSentFile(originalPath, sentMessage.media.photo, 0);
@ -1459,333 +1285,6 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
}
}
}
} else if (file != null) {
if (newMsg.media instanceof TLRPC.TL_messageMediaPhoto && newMsg.media.photo != null) {
TLRPC.PhotoSize size = newMsg.media.photo.sizes.get(newMsg.media.photo.sizes.size() - 1);
String fileName = size.location.volume_id + "_" + size.location.local_id;
size.location = new TLRPC.TL_fileEncryptedLocation();
size.location.key = decryptedMessage.media.key;
size.location.iv = decryptedMessage.media.iv;
size.location.dc_id = file.dc_id;
size.location.volume_id = file.id;
size.location.secret = file.access_hash;
size.location.local_id = file.key_fingerprint;
String fileName2 = size.location.volume_id + "_" + size.location.local_id;
File cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName + ".jpg");
File cacheFile2 = FileLoader.getPathToAttach(size);
cacheFile.renameTo(cacheFile2);
ImageLoader.getInstance().replaceImageInCache(fileName, fileName2);
ArrayList<TLRPC.Message> arr = new ArrayList<TLRPC.Message>();
arr.add(newMsg);
MessagesStorage.getInstance().putMessages(arr, false, true, false, 0);
MessagesStorage.getInstance().putSentFile(originalPath, newMsg.media.photo, 3);
} else if (newMsg.media instanceof TLRPC.TL_messageMediaVideo && newMsg.media.video != null) {
TLRPC.Video video = newMsg.media.video;
newMsg.media.video = new TLRPC.TL_videoEncrypted();
newMsg.media.video.duration = video.duration;
newMsg.media.video.thumb = video.thumb;
newMsg.media.video.dc_id = file.dc_id;
newMsg.media.video.w = video.w;
newMsg.media.video.h = video.h;
newMsg.media.video.date = video.date;
newMsg.media.video.caption = "";
newMsg.media.video.user_id = video.user_id;
newMsg.media.video.size = file.size;
newMsg.media.video.id = file.id;
newMsg.media.video.access_hash = file.access_hash;
newMsg.media.video.key = decryptedMessage.media.key;
newMsg.media.video.iv = decryptedMessage.media.iv;
newMsg.media.video.mime_type = video.mime_type;
if (newMsg.attachPath != null && newMsg.attachPath.startsWith(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE).getAbsolutePath())) {
File cacheFile = new File(newMsg.attachPath);
File cacheFile2 = FileLoader.getPathToAttach(newMsg.media.video);
if (cacheFile.renameTo(cacheFile2)) {
newMsg.attachPath = "";
}
}
ArrayList<TLRPC.Message> arr = new ArrayList<TLRPC.Message>();
arr.add(newMsg);
MessagesStorage.getInstance().putMessages(arr, false, true, false, 0);
MessagesStorage.getInstance().putSentFile(originalPath, newMsg.media.video, 5);
} else if (newMsg.media instanceof TLRPC.TL_messageMediaDocument && newMsg.media.document != null) {
TLRPC.Document document = newMsg.media.document;
newMsg.media.document = new TLRPC.TL_documentEncrypted();
newMsg.media.document.id = file.id;
newMsg.media.document.access_hash = file.access_hash;
newMsg.media.document.user_id = document.user_id;
newMsg.media.document.date = document.date;
newMsg.media.document.file_name = document.file_name;
newMsg.media.document.mime_type = document.mime_type;
newMsg.media.document.size = file.size;
newMsg.media.document.key = decryptedMessage.media.key;
newMsg.media.document.iv = decryptedMessage.media.iv;
newMsg.media.document.thumb = document.thumb;
newMsg.media.document.dc_id = file.dc_id;
if (newMsg.attachPath != null && newMsg.attachPath.startsWith(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE).getAbsolutePath())) {
File cacheFile = new File(newMsg.attachPath);
File cacheFile2 = FileLoader.getPathToAttach(newMsg.media.document);
if (cacheFile.renameTo(cacheFile2)) {
newMsg.attachPath = "";
}
}
ArrayList<TLRPC.Message> arr = new ArrayList<TLRPC.Message>();
arr.add(newMsg);
MessagesStorage.getInstance().putMessages(arr, false, true, false, 0);
MessagesStorage.getInstance().putSentFile(originalPath, newMsg.media.document, 4);
} else if (newMsg.media instanceof TLRPC.TL_messageMediaAudio && newMsg.media.audio != null) {
TLRPC.Audio audio = newMsg.media.audio;
newMsg.media.audio = new TLRPC.TL_audioEncrypted();
newMsg.media.audio.id = file.id;
newMsg.media.audio.access_hash = file.access_hash;
newMsg.media.audio.user_id = audio.user_id;
newMsg.media.audio.date = audio.date;
newMsg.media.audio.duration = audio.duration;
newMsg.media.audio.size = file.size;
newMsg.media.audio.dc_id = file.dc_id;
newMsg.media.audio.key = decryptedMessage.media.key;
newMsg.media.audio.iv = decryptedMessage.media.iv;
newMsg.media.audio.mime_type = audio.mime_type;
String fileName = audio.dc_id + "_" + audio.id + ".ogg";
String fileName2 = newMsg.media.audio.dc_id + "_" + newMsg.media.audio.id + ".ogg";
if (!fileName.equals(fileName2)) {
File cacheFile = new File(FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName);
File cacheFile2 = FileLoader.getPathToAttach(newMsg.media.audio);
if (cacheFile.renameTo(cacheFile2)) {
newMsg.attachPath = "";
}
}
ArrayList<TLRPC.Message> arr = new ArrayList<TLRPC.Message>();
arr.add(newMsg);
MessagesStorage.getInstance().putMessages(arr, false, true, false, 0);
}
}
}
private TLRPC.TL_messageService createServiceSecretMessage(final TLRPC.EncryptedChat encryptedChat, TLRPC.DecryptedMessageAction decryptedMessage) {
TLRPC.TL_messageService newMsg = new TLRPC.TL_messageService();
newMsg.action = new TLRPC.TL_messageEncryptedAction();
newMsg.action.encryptedAction = decryptedMessage;
newMsg.local_id = newMsg.id = UserConfig.getNewMessageId();
newMsg.from_id = UserConfig.getClientUserId();
newMsg.flags = TLRPC.MESSAGE_FLAG_UNREAD | TLRPC.MESSAGE_FLAG_OUT;
newMsg.dialog_id = ((long)encryptedChat.id) << 32;
newMsg.to_id = new TLRPC.TL_peerUser();
newMsg.send_state = MessageObject.MESSAGE_SEND_STATE_SENDING;
if (encryptedChat.participant_id == UserConfig.getClientUserId()) {
newMsg.to_id.user_id = encryptedChat.admin_id;
} else {
newMsg.to_id.user_id = encryptedChat.participant_id;
}
if (decryptedMessage instanceof TLRPC.TL_decryptedMessageActionScreenshotMessages || decryptedMessage instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL) {
newMsg.date = ConnectionsManager.getInstance().getCurrentTime();
} else {
newMsg.date = 0;
}
newMsg.random_id = getNextRandomId();
UserConfig.saveConfig(false);
ArrayList<TLRPC.Message> arr = new ArrayList<TLRPC.Message>();
arr.add(newMsg);
MessagesStorage.getInstance().putMessages(arr, false, true, true, 0);
return newMsg;
}
public void sendMessagesReadMessage(TLRPC.EncryptedChat encryptedChat, ArrayList<Long> random_ids, TLRPC.Message resendMessage) {
if (!(encryptedChat instanceof TLRPC.TL_encryptedChat)) {
return;
}
TLRPC.TL_decryptedMessageService reqSend = null;
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
reqSend = new TLRPC.TL_decryptedMessageService();
} else {
reqSend = new TLRPC.TL_decryptedMessageService_old();
reqSend.random_bytes = new byte[Math.max(1, (int) Math.ceil(Utilities.random.nextDouble() * 16))];
Utilities.random.nextBytes(reqSend.random_bytes);
}
TLRPC.Message message = null;
if (resendMessage != null) {
message = resendMessage;
reqSend.action = message.action.encryptedAction;
} else {
reqSend.action = new TLRPC.TL_decryptedMessageActionReadMessages();
reqSend.action.random_ids = random_ids;
message = createServiceSecretMessage(encryptedChat, reqSend.action);
}
reqSend.random_id = message.random_id;
performSendEncryptedRequest(reqSend, message, encryptedChat, null, null);
}
public void sendMessagesDeleteMessage(TLRPC.EncryptedChat encryptedChat, ArrayList<Long> random_ids, TLRPC.Message resendMessage) {
if (!(encryptedChat instanceof TLRPC.TL_encryptedChat)) {
return;
}
TLRPC.TL_decryptedMessageService reqSend = null;
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
reqSend = new TLRPC.TL_decryptedMessageService();
} else {
reqSend = new TLRPC.TL_decryptedMessageService_old();
reqSend.random_bytes = new byte[Math.max(1, (int) Math.ceil(Utilities.random.nextDouble() * 16))];
Utilities.random.nextBytes(reqSend.random_bytes);
}
TLRPC.Message message = null;
if (resendMessage != null) {
message = resendMessage;
reqSend.action = message.action.encryptedAction;
} else {
reqSend.action = new TLRPC.TL_decryptedMessageActionDeleteMessages();
reqSend.action.random_ids = random_ids;
message = createServiceSecretMessage(encryptedChat, reqSend.action);
}
reqSend.random_id = message.random_id;
performSendEncryptedRequest(reqSend, message, encryptedChat, null, null);
}
public void sendClearHistoryMessage(TLRPC.EncryptedChat encryptedChat, TLRPC.Message resendMessage) {
if (!(encryptedChat instanceof TLRPC.TL_encryptedChat)) {
return;
}
TLRPC.TL_decryptedMessageService reqSend = null;
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
reqSend = new TLRPC.TL_decryptedMessageService();
} else {
reqSend = new TLRPC.TL_decryptedMessageService_old();
reqSend.random_bytes = new byte[Math.max(1, (int) Math.ceil(Utilities.random.nextDouble() * 16))];
Utilities.random.nextBytes(reqSend.random_bytes);
}
TLRPC.Message message = null;
if (resendMessage != null) {
message = resendMessage;
reqSend.action = message.action.encryptedAction;
} else {
reqSend.action = new TLRPC.TL_decryptedMessageActionFlushHistory();
message = createServiceSecretMessage(encryptedChat, reqSend.action);
}
reqSend.random_id = message.random_id;
performSendEncryptedRequest(reqSend, message, encryptedChat, null, null);
}
public void sendNotifyLayerMessage(final TLRPC.EncryptedChat encryptedChat, TLRPC.Message resendMessage) {
if (!(encryptedChat instanceof TLRPC.TL_encryptedChat)) {
return;
}
if (sendingNotifyLayer.contains(encryptedChat.id)) {
return;
}
sendingNotifyLayer.add(encryptedChat.id);
TLRPC.TL_decryptedMessageService reqSend = null;
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
reqSend = new TLRPC.TL_decryptedMessageService();
} else {
reqSend = new TLRPC.TL_decryptedMessageService_old();
reqSend.random_bytes = new byte[Math.max(1, (int) Math.ceil(Utilities.random.nextDouble() * 16))];
Utilities.random.nextBytes(reqSend.random_bytes);
}
TLRPC.Message message = null;
if (resendMessage != null) {
message = resendMessage;
reqSend.action = message.action.encryptedAction;
} else {
reqSend.action = new TLRPC.TL_decryptedMessageActionNotifyLayer();
reqSend.action.layer = CURRENT_SECRET_CHAT_LAYER;
message = createServiceSecretMessage(encryptedChat, reqSend.action);
}
reqSend.random_id = message.random_id;
performSendEncryptedRequest(reqSend, message, encryptedChat, null, null);
}
public void sendTTLMessage(TLRPC.EncryptedChat encryptedChat, TLRPC.Message resendMessage) {
if (!(encryptedChat instanceof TLRPC.TL_encryptedChat)) {
return;
}
TLRPC.TL_decryptedMessageService reqSend = null;
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
reqSend = new TLRPC.TL_decryptedMessageService();
} else {
reqSend = new TLRPC.TL_decryptedMessageService_old();
reqSend.random_bytes = new byte[Math.max(1, (int) Math.ceil(Utilities.random.nextDouble() * 16))];
Utilities.random.nextBytes(reqSend.random_bytes);
}
TLRPC.Message message = null;
if (resendMessage != null) {
message = resendMessage;
reqSend.action = message.action.encryptedAction;
} else {
reqSend.action = new TLRPC.TL_decryptedMessageActionSetMessageTTL();
reqSend.action.ttl_seconds = encryptedChat.ttl;
message = createServiceSecretMessage(encryptedChat, reqSend.action);
MessageObject newMsgObj = new MessageObject(message, null);
newMsgObj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SENDING;
ArrayList<MessageObject> objArr = new ArrayList<MessageObject>();
objArr.add(newMsgObj);
MessagesController.getInstance().updateInterfaceWithMessages(message.dialog_id, objArr);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload);
}
reqSend.random_id = message.random_id;
performSendEncryptedRequest(reqSend, message, encryptedChat, null, null);
}
public void sendScreenshotMessage(TLRPC.EncryptedChat encryptedChat, ArrayList<Long> random_ids, TLRPC.Message resendMessage) {
if (!(encryptedChat instanceof TLRPC.TL_encryptedChat)) {
return;
}
TLRPC.TL_decryptedMessageService reqSend = null;
if (AndroidUtilities.getPeerLayerVersion(encryptedChat.layer) >= 17) {
reqSend = new TLRPC.TL_decryptedMessageService();
} else {
reqSend = new TLRPC.TL_decryptedMessageService_old();
reqSend.random_bytes = new byte[Math.max(1, (int) Math.ceil(Utilities.random.nextDouble() * 16))];
Utilities.random.nextBytes(reqSend.random_bytes);
}
TLRPC.Message message = null;
if (resendMessage != null) {
message = resendMessage;
reqSend.action = message.action.encryptedAction;
} else {
reqSend.action = new TLRPC.TL_decryptedMessageActionScreenshotMessages();
reqSend.action.random_ids = random_ids;
message = createServiceSecretMessage(encryptedChat, reqSend.action);
MessageObject newMsgObj = new MessageObject(message, null);
newMsgObj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SENDING;
ArrayList<MessageObject> objArr = new ArrayList<MessageObject>();
objArr.add(newMsgObj);
MessagesController.getInstance().updateInterfaceWithMessages(message.dialog_id, objArr);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.dialogsNeedReload);
}
reqSend.random_id = message.random_id;
performSendEncryptedRequest(reqSend, message, encryptedChat, null, null);
}
private void putToDelayedMessages(String location, DelayedMessage message) {
@ -1797,7 +1296,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
arrayList.add(message);
}
private long getNextRandomId() {
protected long getNextRandomId() {
long val = 0;
while (val == 0) {
val = Utilities.random.nextLong();
@ -1810,7 +1309,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
}
protected void processUnsentMessages(final ArrayList<TLRPC.Message> messages, final ArrayList<TLRPC.User> users, final ArrayList<TLRPC.Chat> chats, final ArrayList<TLRPC.EncryptedChat> encryptedChats) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
MessagesController.getInstance().putUsers(users, true);
@ -1856,13 +1355,27 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
}
}
private static void prepareSendingDocumentInternal(final String path, String originalPath, final long dialog_id) {
if (path == null || path.length() == 0) {
return;
private static boolean prepareSendingDocumentInternal(String path, String originalPath, Uri uri, String mime, final long dialog_id) {
if ((path == null || path.length() == 0) && uri == null) {
return false;
}
MimeTypeMap myMime = MimeTypeMap.getSingleton();
if (uri != null) {
String extension = null;
if (mime != null) {
extension = myMime.getExtensionFromMimeType(mime);
}
if (extension == null) {
extension = "txt";
}
path = MediaController.copyDocumentToCache(uri, extension);
if (path == null) {
return false;
}
}
final File f = new File(path);
if (!f.exists() || f.length() == 0) {
return;
return false;
}
boolean isEncrypted = (int)dialog_id == 0;
@ -1893,7 +1406,6 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
document.size = (int)f.length();
document.dc_id = 0;
if (ext.length() != 0) {
MimeTypeMap myMime = MimeTypeMap.getSingleton();
String mimeType = myMime.getMimeTypeFromExtension(ext.toLowerCase());
if (mimeType != null) {
document.mime_type = mimeType;
@ -1921,34 +1433,65 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
final TLRPC.TL_document documentFinal = document;
final String originalPathFinal = originalPath;
AndroidUtilities.RunOnUIThread(new Runnable() {
final String pathFinal = path;
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
SendMessagesHelper.getInstance().sendMessage(documentFinal, originalPathFinal, path, dialog_id);
SendMessagesHelper.getInstance().sendMessage(documentFinal, originalPathFinal, pathFinal, dialog_id);
}
});
return true;
}
public static void prepareSendingDocument(String path, String originalPath, long dialog_id) {
if (path == null || originalPath == null) {
public static void prepareSendingDocument(String path, String originalPath, Uri uri, String mine, long dialog_id) {
if ((path == null || originalPath == null) && uri == null) {
return;
}
ArrayList<String> paths = new ArrayList<String>();
ArrayList<String> originalPaths = new ArrayList<String>();
ArrayList<Uri> uris = null;
if (uri != null) {
uris = new ArrayList<Uri>();
}
paths.add(path);
originalPaths.add(originalPath);
prepareSendingDocuments(paths, originalPaths, dialog_id);
prepareSendingDocuments(paths, originalPaths, uris, mine, dialog_id);
}
public static void prepareSendingDocuments(final ArrayList<String> paths, final ArrayList<String> originalPaths, final long dialog_id) {
if (paths == null && originalPaths == null || paths != null && originalPaths != null && paths.size() != originalPaths.size()) {
public static void prepareSendingDocuments(final ArrayList<String> paths, final ArrayList<String> originalPaths, final ArrayList<Uri> uris, final String mime, final long dialog_id) {
if (paths == null && originalPaths == null && uris == null || paths != null && originalPaths != null && paths.size() != originalPaths.size()) {
return;
}
new Thread(new Runnable() {
@Override
public void run() {
boolean error = false;
if (paths != null) {
for (int a = 0; a < paths.size(); a++) {
prepareSendingDocumentInternal(paths.get(a), originalPaths.get(a), dialog_id);
if (!prepareSendingDocumentInternal(paths.get(a), originalPaths.get(a), null, mime, dialog_id)) {
error = true;
}
}
}
if (uris != null) {
for (int a = 0; a < uris.size(); a++) {
if (!prepareSendingDocumentInternal(null, null, uris.get(a), mime, dialog_id)) {
error = true;
}
}
}
if (error) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
try {
Toast toast = Toast.makeText(ApplicationLoader.applicationContext, LocaleController.getString("UnsupportedAttachment", R.string.UnsupportedAttachment), Toast.LENGTH_SHORT);
toast.show();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
});
}
}
}).start();
@ -2039,7 +1582,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
if (photo != null) {
final String originalPathFinal = originalPath;
final TLRPC.TL_photo photoFinal = photo;
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
SendMessagesHelper.getInstance().sendMessage(photoFinal, originalPathFinal, dialog_id);
@ -2050,7 +1593,7 @@ 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), dialog_id);
prepareSendingDocumentInternal(sendAsDocuments.get(a), sendAsDocumentsOriginal.get(a), null, "gif", dialog_id);
}
}
}
@ -2161,7 +1704,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
final TLRPC.TL_video videoFinal = video;
final String originalPathFinal = originalPath;
final String finalPath = path;
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
SendMessagesHelper.getInstance().sendMessage(videoFinal, originalPathFinal, finalPath, dialog_id);

View File

@ -48,7 +48,7 @@ public class SmsListener extends BroadcastReceiver {
if (matcher.find()) {
String str = matcher.group(0);
if (str.length() >= 3) {
NotificationCenter.getInstance().postNotificationName(998, matcher.group(0));
NotificationCenter.getInstance().postNotificationName(NotificationCenter.didReceiveSmsCode, matcher.group(0));
}
}
} catch (Exception e) {

View File

@ -16,7 +16,7 @@ import android.support.v4.app.NotificationManagerCompat;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.R;
import org.telegram.ui.ApplicationLoader;
import org.telegram.messenger.ApplicationLoader;
public class VideoEncodingService extends Service implements NotificationCenter.NotificationCenterDelegate {

View File

@ -0,0 +1,107 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.android.time;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* <p>DateParser is the "missing" interface for the parsing methods of
* {@link java.text.DateFormat}.</p>
*
* @since 3.2
*/
public interface DateParser {
/**
* Equivalent to DateFormat.parse(String).
* <p/>
* See {@link java.text.DateFormat#parse(String)} for more information.
*
* @param source A <code>String</code> whose beginning should be parsed.
* @return A <code>Date</code> parsed from the string
* @throws ParseException if the beginning of the specified string cannot be parsed.
*/
Date parse(String source) throws ParseException;
/**
* Equivalent to DateFormat.parse(String, ParsePosition).
* <p/>
* See {@link java.text.DateFormat#parse(String, ParsePosition)} for more information.
*
* @param source A <code>String</code>, part of which should be parsed.
* @param pos A <code>ParsePosition</code> object with index and error index information
* as described above.
* @return A <code>Date</code> parsed from the string. In case of error, returns null.
* @throws NullPointerException if text or pos is null.
*/
Date parse(String source, ParsePosition pos);
// Accessors
//-----------------------------------------------------------------------
/**
* <p>Get the pattern used by this parser.</p>
*
* @return the pattern, {@link java.text.SimpleDateFormat} compatible
*/
String getPattern();
/**
* <p>
* Get the time zone used by this parser.
* </p>
* <p/>
* <p>
* The default {@link TimeZone} used to create a {@link Date} when the {@link TimeZone} is not specified by
* the format pattern.
* </p>
*
* @return the time zone
*/
TimeZone getTimeZone();
/**
* <p>Get the locale used by this parser.</p>
*
* @return the locale
*/
Locale getLocale();
/**
* Parses text from a string to produce a Date.
*
* @param source A <code>String</code> whose beginning should be parsed.
* @return a <code>java.util.Date</code> object
* @throws ParseException if the beginning of the specified string cannot be parsed.
* @see java.text.DateFormat#parseObject(String)
*/
Object parseObject(String source) throws ParseException;
/**
* Parse a date/time string according to the given parse position.
*
* @param source A <code>String</code> whose beginning should be parsed.
* @param pos the parse position
* @return a <code>java.util.Date</code> object
* @see java.text.DateFormat#parseObject(String, ParsePosition)
*/
Object parseObject(String source, ParsePosition pos);
}

View File

@ -0,0 +1,126 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.android.time;
import java.text.FieldPosition;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* <p>DatePrinter is the "missing" interface for the format methods of
* {@link java.text.DateFormat}.</p>
*
* @since 3.2
*/
public interface DatePrinter {
/**
* <p>Formats a millisecond {@code long} value.</p>
*
* @param millis the millisecond value to format
* @return the formatted string
* @since 2.1
*/
String format(long millis);
/**
* <p>Formats a {@code Date} object using a {@code GregorianCalendar}.</p>
*
* @param date the date to format
* @return the formatted string
*/
String format(Date date);
/**
* <p>Formats a {@code Calendar} object.</p>
*
* @param calendar the calendar to format
* @return the formatted string
*/
String format(Calendar calendar);
/**
* <p>Formats a milliseond {@code long} value into the
* supplied {@code StringBuffer}.</p>
*
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @return the specified string buffer
*/
StringBuffer format(long millis, StringBuffer buf);
/**
* <p>Formats a {@code Date} object into the
* supplied {@code StringBuffer} using a {@code GregorianCalendar}.</p>
*
* @param date the date to format
* @param buf the buffer to format into
* @return the specified string buffer
*/
StringBuffer format(Date date, StringBuffer buf);
/**
* <p>Formats a {@code Calendar} object into the
* supplied {@code StringBuffer}.</p>
*
* @param calendar the calendar to format
* @param buf the buffer to format into
* @return the specified string buffer
*/
StringBuffer format(Calendar calendar, StringBuffer buf);
// Accessors
//-----------------------------------------------------------------------
/**
* <p>Gets the pattern used by this printer.</p>
*
* @return the pattern, {@link java.text.SimpleDateFormat} compatible
*/
String getPattern();
/**
* <p>Gets the time zone used by this printer.</p>
* <p/>
* <p>This zone is always used for {@code Date} printing. </p>
*
* @return the time zone
*/
TimeZone getTimeZone();
/**
* <p>Gets the locale used by this printer.</p>
*
* @return the locale
*/
Locale getLocale();
/**
* <p>Formats a {@code Date}, {@code Calendar} or
* {@code Long} (milliseconds) object.</p>
* <p/>
* See {@link java.text.DateFormat#format(Object, StringBuffer, FieldPosition)}
*
* @param obj the object to format
* @param toAppendTo the buffer to append to
* @param pos the position - ignored
* @return the buffer passed in
*/
StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos);
}

View File

@ -0,0 +1,613 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.android.time;
import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* <p>FastDateFormat is a fast and thread-safe version of
* {@link java.text.SimpleDateFormat}.</p>
*
* <p>This class can be used as a direct replacement to
* {@code SimpleDateFormat} in most formatting and parsing situations.
* This class is especially useful in multi-threaded server environments.
* {@code SimpleDateFormat} is not thread-safe in any JDK version,
* nor will it be as Sun have closed the bug/RFE.
* </p>
*
* <p>All patterns are compatible with
* SimpleDateFormat (except time zones and some year patterns - see below).</p>
*
* <p>Since 3.2, FastDateFormat supports parsing as well as printing.</p>
*
* <p>Java 1.4 introduced a new pattern letter, {@code 'Z'}, to represent
* time zones in RFC822 format (eg. {@code +0800} or {@code -1100}).
* This pattern letter can be used here (on all JDK versions).</p>
*
* <p>In addition, the pattern {@code 'ZZ'} has been made to represent
* ISO8601 full format time zones (eg. {@code +08:00} or {@code -11:00}).
* This introduces a minor incompatibility with Java 1.4, but at a gain of
* useful functionality.</p>
*
* <p>Javadoc cites for the year pattern: <i>For formatting, if the number of
* pattern letters is 2, the year is truncated to 2 digits; otherwise it is
* interpreted as a number.</i> Starting with Java 1.7 a pattern of 'Y' or
* 'YYY' will be formatted as '2003', while it was '03' in former Java
* versions. FastDateFormat implements the behavior of Java 7.</p>
*
* @since 2.0
* @version $Id: FastDateFormat.java 1572877 2014-02-28 08:42:25Z britter $
*/
public class FastDateFormat extends Format implements DateParser, DatePrinter {
/**
* Required for serialization support.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = 2L;
/**
* FULL locale dependent date or time style.
*/
public static final int FULL = DateFormat.FULL;
/**
* LONG locale dependent date or time style.
*/
public static final int LONG = DateFormat.LONG;
/**
* MEDIUM locale dependent date or time style.
*/
public static final int MEDIUM = DateFormat.MEDIUM;
/**
* SHORT locale dependent date or time style.
*/
public static final int SHORT = DateFormat.SHORT;
private static final FormatCache<FastDateFormat> cache = new FormatCache<FastDateFormat>() {
@Override
protected FastDateFormat createInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
return new FastDateFormat(pattern, timeZone, locale);
}
};
private final FastDatePrinter printer;
private final FastDateParser parser;
//-----------------------------------------------------------------------
/**
* <p>Gets a formatter instance using the default pattern in the
* default locale.</p>
*
* @return a date/time formatter
*/
public static FastDateFormat getInstance() {
return cache.getInstance();
}
/**
* <p>Gets a formatter instance using the specified pattern in the
* default locale.</p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible
* pattern
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
*/
public static FastDateFormat getInstance(final String pattern) {
return cache.getInstance(pattern, null, null);
}
/**
* <p>Gets a formatter instance using the specified pattern and
* time zone.</p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible
* pattern
* @param timeZone optional time zone, overrides time zone of
* formatted date
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
*/
public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone) {
return cache.getInstance(pattern, timeZone, null);
}
/**
* <p>Gets a formatter instance using the specified pattern and
* locale.</p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible
* pattern
* @param locale optional locale, overrides system locale
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
*/
public static FastDateFormat getInstance(final String pattern, final Locale locale) {
return cache.getInstance(pattern, null, locale);
}
/**
* <p>Gets a formatter instance using the specified pattern, time zone
* and locale.</p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible
* pattern
* @param timeZone optional time zone, overrides time zone of
* formatted date
* @param locale optional locale, overrides system locale
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
* or {@code null}
*/
public static FastDateFormat getInstance(final String pattern, final TimeZone timeZone, final Locale locale) {
return cache.getInstance(pattern, timeZone, locale);
}
//-----------------------------------------------------------------------
/**
* <p>Gets a date formatter instance using the specified style in the
* default time zone and locale.</p>
*
* @param style date style: FULL, LONG, MEDIUM, or SHORT
* @return a localized standard date formatter
* @throws IllegalArgumentException if the Locale has no date
* pattern defined
* @since 2.1
*/
public static FastDateFormat getDateInstance(final int style) {
return cache.getDateInstance(style, null, null);
}
/**
* <p>Gets a date formatter instance using the specified style and
* locale in the default time zone.</p>
*
* @param style date style: FULL, LONG, MEDIUM, or SHORT
* @param locale optional locale, overrides system locale
* @return a localized standard date formatter
* @throws IllegalArgumentException if the Locale has no date
* pattern defined
* @since 2.1
*/
public static FastDateFormat getDateInstance(final int style, final Locale locale) {
return cache.getDateInstance(style, null, locale);
}
/**
* <p>Gets a date formatter instance using the specified style and
* time zone in the default locale.</p>
*
* @param style date style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted date
* @return a localized standard date formatter
* @throws IllegalArgumentException if the Locale has no date
* pattern defined
* @since 2.1
*/
public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone) {
return cache.getDateInstance(style, timeZone, null);
}
/**
* <p>Gets a date formatter instance using the specified style, time
* zone and locale.</p>
*
* @param style date style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted date
* @param locale optional locale, overrides system locale
* @return a localized standard date formatter
* @throws IllegalArgumentException if the Locale has no date
* pattern defined
*/
public static FastDateFormat getDateInstance(final int style, final TimeZone timeZone, final Locale locale) {
return cache.getDateInstance(style, timeZone, locale);
}
//-----------------------------------------------------------------------
/**
* <p>Gets a time formatter instance using the specified style in the
* default time zone and locale.</p>
*
* @param style time style: FULL, LONG, MEDIUM, or SHORT
* @return a localized standard time formatter
* @throws IllegalArgumentException if the Locale has no time
* pattern defined
* @since 2.1
*/
public static FastDateFormat getTimeInstance(final int style) {
return cache.getTimeInstance(style, null, null);
}
/**
* <p>Gets a time formatter instance using the specified style and
* locale in the default time zone.</p>
*
* @param style time style: FULL, LONG, MEDIUM, or SHORT
* @param locale optional locale, overrides system locale
* @return a localized standard time formatter
* @throws IllegalArgumentException if the Locale has no time
* pattern defined
* @since 2.1
*/
public static FastDateFormat getTimeInstance(final int style, final Locale locale) {
return cache.getTimeInstance(style, null, locale);
}
/**
* <p>Gets a time formatter instance using the specified style and
* time zone in the default locale.</p>
*
* @param style time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted time
* @return a localized standard time formatter
* @throws IllegalArgumentException if the Locale has no time
* pattern defined
* @since 2.1
*/
public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone) {
return cache.getTimeInstance(style, timeZone, null);
}
/**
* <p>Gets a time formatter instance using the specified style, time
* zone and locale.</p>
*
* @param style time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted time
* @param locale optional locale, overrides system locale
* @return a localized standard time formatter
* @throws IllegalArgumentException if the Locale has no time
* pattern defined
*/
public static FastDateFormat getTimeInstance(final int style, final TimeZone timeZone, final Locale locale) {
return cache.getTimeInstance(style, timeZone, locale);
}
//-----------------------------------------------------------------------
/**
* <p>Gets a date/time formatter instance using the specified style
* in the default time zone and locale.</p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time
* pattern defined
* @since 2.1
*/
public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle) {
return cache.getDateTimeInstance(dateStyle, timeStyle, null, null);
}
/**
* <p>Gets a date/time formatter instance using the specified style and
* locale in the default time zone.</p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time
* pattern defined
* @since 2.1
*/
public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final Locale locale) {
return cache.getDateTimeInstance(dateStyle, timeStyle, null, locale);
}
/**
* <p>Gets a date/time formatter instance using the specified style and
* time zone in the default locale.</p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted date
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time
* pattern defined
* @since 2.1
*/
public static FastDateFormat getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone) {
return getDateTimeInstance(dateStyle, timeStyle, timeZone, null);
}
/**
* <p>Gets a date/time formatter instance using the specified style,
* time zone and locale.</p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted date
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time
* pattern defined
*/
public static FastDateFormat getDateTimeInstance(
final int dateStyle, final int timeStyle, final TimeZone timeZone, final Locale locale) {
return cache.getDateTimeInstance(dateStyle, timeStyle, timeZone, locale);
}
// Constructor
//-----------------------------------------------------------------------
/**
* <p>Constructs a new FastDateFormat.</p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern
* @param timeZone non-null time zone to use
* @param locale non-null locale to use
* @throws NullPointerException if pattern, timeZone, or locale is null.
*/
protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale) {
this(pattern, timeZone, locale, null);
}
// Constructor
//-----------------------------------------------------------------------
/**
* <p>Constructs a new FastDateFormat.</p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern
* @param timeZone non-null time zone to use
* @param locale non-null locale to use
* @param centuryStart The start of the 100 year period to use as the "default century" for 2 digit year parsing. If centuryStart is null, defaults to now - 80 years
* @throws NullPointerException if pattern, timeZone, or locale is null.
*/
protected FastDateFormat(final String pattern, final TimeZone timeZone, final Locale locale, final Date centuryStart) {
printer = new FastDatePrinter(pattern, timeZone, locale);
parser = new FastDateParser(pattern, timeZone, locale, centuryStart);
}
// Format methods
//-----------------------------------------------------------------------
/**
* <p>Formats a {@code Date}, {@code Calendar} or
* {@code Long} (milliseconds) object.</p>
*
* @param obj the object to format
* @param toAppendTo the buffer to append to
* @param pos the position - ignored
* @return the buffer passed in
*/
@Override
public StringBuffer format(final Object obj, final StringBuffer toAppendTo, final FieldPosition pos) {
return printer.format(obj, toAppendTo, pos);
}
/**
* <p>Formats a millisecond {@code long} value.</p>
*
* @param millis the millisecond value to format
* @return the formatted string
* @since 2.1
*/
@Override
public String format(final long millis) {
return printer.format(millis);
}
/**
* <p>Formats a {@code Date} object using a {@code GregorianCalendar}.</p>
*
* @param date the date to format
* @return the formatted string
*/
@Override
public String format(final Date date) {
return printer.format(date);
}
/**
* <p>Formats a {@code Calendar} object.</p>
*
* @param calendar the calendar to format
* @return the formatted string
*/
@Override
public String format(final Calendar calendar) {
return printer.format(calendar);
}
/**
* <p>Formats a millisecond {@code long} value into the
* supplied {@code StringBuffer}.</p>
*
* @param millis the millisecond value to format
* @param buf the buffer to format into
* @return the specified string buffer
* @since 2.1
*/
@Override
public StringBuffer format(final long millis, final StringBuffer buf) {
return printer.format(millis, buf);
}
/**
* <p>Formats a {@code Date} object into the
* supplied {@code StringBuffer} using a {@code GregorianCalendar}.</p>
*
* @param date the date to format
* @param buf the buffer to format into
* @return the specified string buffer
*/
@Override
public StringBuffer format(final Date date, final StringBuffer buf) {
return printer.format(date, buf);
}
/**
* <p>Formats a {@code Calendar} object into the
* supplied {@code StringBuffer}.</p>
*
* @param calendar the calendar to format
* @param buf the buffer to format into
* @return the specified string buffer
*/
@Override
public StringBuffer format(final Calendar calendar, final StringBuffer buf) {
return printer.format(calendar, buf);
}
// Parsing
//-----------------------------------------------------------------------
/* (non-Javadoc)
* @see DateParser#parse(java.lang.String)
*/
@Override
public Date parse(final String source) throws ParseException {
return parser.parse(source);
}
/* (non-Javadoc)
* @see DateParser#parse(java.lang.String, java.text.ParsePosition)
*/
@Override
public Date parse(final String source, final ParsePosition pos) {
return parser.parse(source, pos);
}
/* (non-Javadoc)
* @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
*/
@Override
public Object parseObject(final String source, final ParsePosition pos) {
return parser.parseObject(source, pos);
}
// Accessors
//-----------------------------------------------------------------------
/**
* <p>Gets the pattern used by this formatter.</p>
*
* @return the pattern, {@link java.text.SimpleDateFormat} compatible
*/
@Override
public String getPattern() {
return printer.getPattern();
}
/**
* <p>Gets the time zone used by this formatter.</p>
* <p/>
* <p>This zone is always used for {@code Date} formatting. </p>
*
* @return the time zone
*/
@Override
public TimeZone getTimeZone() {
return printer.getTimeZone();
}
/**
* <p>Gets the locale used by this formatter.</p>
*
* @return the locale
*/
@Override
public Locale getLocale() {
return printer.getLocale();
}
/**
* <p>Gets an estimate for the maximum string length that the
* formatter will produce.</p>
* <p/>
* <p>The actual formatted length will almost always be less than or
* equal to this amount.</p>
*
* @return the maximum formatted length
*/
public int getMaxLengthEstimate() {
return printer.getMaxLengthEstimate();
}
// Basics
//-----------------------------------------------------------------------
/**
* <p>Compares two objects for equality.</p>
*
* @param obj the object to compare to
* @return {@code true} if equal
*/
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof FastDateFormat)) {
return false;
}
final FastDateFormat other = (FastDateFormat) obj;
// no need to check parser, as it has same invariants as printer
return printer.equals(other.printer);
}
/**
* <p>Returns a hashcode compatible with equals.</p>
*
* @return a hashcode compatible with equals
*/
@Override
public int hashCode() {
return printer.hashCode();
}
/**
* <p>Gets a debugging string version of this formatter.</p>
*
* @return a debugging string
*/
@Override
public String toString() {
return "FastDateFormat[" + printer.getPattern() + "," + printer.getLocale() + "," + printer.getTimeZone().getID() + "]";
}
/**
* <p>Performs the formatting by applying the rules to the
* specified calendar.</p>
*
* @param calendar the calendar to format
* @param buf the buffer to format into
* @return the specified string buffer
*/
protected StringBuffer applyRules(final Calendar calendar, final StringBuffer buf) {
return printer.applyRules(calendar, buf);
}
}

View File

@ -0,0 +1,873 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.android.time;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.text.DateFormatSymbols;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.SortedMap;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <p>FastDateParser is a fast and thread-safe version of
* {@link java.text.SimpleDateFormat}.</p>
*
* <p>This class can be used as a direct replacement for
* <code>SimpleDateFormat</code> in most parsing situations.
* This class is especially useful in multi-threaded server environments.
* <code>SimpleDateFormat</code> is not thread-safe in any JDK version,
* nor will it be as Sun has closed the
* <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4228335">bug</a>/RFE.
* </p>
*
* <p>Only parsing is supported, but all patterns are compatible with
* SimpleDateFormat.</p>
*
* <p>Timing tests indicate this class is as about as fast as SimpleDateFormat
* in single thread applications and about 25% faster in multi-thread applications.</p>
*
* @version $Id: FastDateParser.java 1572877 2014-02-28 08:42:25Z britter $
* @since 3.2
*/
public class FastDateParser implements DateParser, Serializable {
/**
* Required for serialization support.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = 2L;
static final Locale JAPANESE_IMPERIAL = new Locale("ja", "JP", "JP");
// defining fields
private final String pattern;
private final TimeZone timeZone;
private final Locale locale;
private final int century;
private final int startYear;
// derived fields
private transient Pattern parsePattern;
private transient Strategy[] strategies;
// dynamic fields to communicate with Strategy
private transient String currentFormatField;
private transient Strategy nextStrategy;
/**
* <p>Constructs a new FastDateParser.</p>
*
* @param pattern non-null {@link java.text.SimpleDateFormat} compatible
* pattern
* @param timeZone non-null time zone to use
* @param locale non-null locale
*/
protected FastDateParser(final String pattern, final TimeZone timeZone, final Locale locale) {
this(pattern, timeZone, locale, null);
}
/**
* <p>Constructs a new FastDateParser.</p>
*
* @param pattern non-null {@link java.text.SimpleDateFormat} compatible
* pattern
* @param timeZone non-null time zone to use
* @param locale non-null locale
* @param centuryStart The start of the century for 2 digit year parsing
* @since 3.3
*/
protected FastDateParser(final String pattern, final TimeZone timeZone, final Locale locale, final Date centuryStart) {
this.pattern = pattern;
this.timeZone = timeZone;
this.locale = locale;
final Calendar definingCalendar = Calendar.getInstance(timeZone, locale);
int centuryStartYear;
if (centuryStart != null) {
definingCalendar.setTime(centuryStart);
centuryStartYear = definingCalendar.get(Calendar.YEAR);
} else if (locale.equals(JAPANESE_IMPERIAL)) {
centuryStartYear = 0;
} else {
// from 80 years ago to 20 years from now
definingCalendar.setTime(new Date());
centuryStartYear = definingCalendar.get(Calendar.YEAR) - 80;
}
century = centuryStartYear / 100 * 100;
startYear = centuryStartYear - century;
init(definingCalendar);
}
/**
* Initialize derived fields from defining fields.
* This is called from constructor and from readObject (de-serialization)
*
* @param definingCalendar the {@link java.util.Calendar} instance used to initialize this FastDateParser
*/
private void init(Calendar definingCalendar) {
final StringBuilder regex = new StringBuilder();
final List<Strategy> collector = new ArrayList<Strategy>();
final Matcher patternMatcher = formatPattern.matcher(pattern);
if (!patternMatcher.lookingAt()) {
throw new IllegalArgumentException(
"Illegal pattern character '" + pattern.charAt(patternMatcher.regionStart()) + "'");
}
currentFormatField = patternMatcher.group();
Strategy currentStrategy = getStrategy(currentFormatField, definingCalendar);
for (; ; ) {
patternMatcher.region(patternMatcher.end(), patternMatcher.regionEnd());
if (!patternMatcher.lookingAt()) {
nextStrategy = null;
break;
}
final String nextFormatField = patternMatcher.group();
nextStrategy = getStrategy(nextFormatField, definingCalendar);
if (currentStrategy.addRegex(this, regex)) {
collector.add(currentStrategy);
}
currentFormatField = nextFormatField;
currentStrategy = nextStrategy;
}
if (patternMatcher.regionStart() != patternMatcher.regionEnd()) {
throw new IllegalArgumentException("Failed to parse \"" + pattern + "\" ; gave up at index " + patternMatcher.regionStart());
}
if (currentStrategy.addRegex(this, regex)) {
collector.add(currentStrategy);
}
currentFormatField = null;
strategies = collector.toArray(new Strategy[collector.size()]);
parsePattern = Pattern.compile(regex.toString());
}
// Accessors
//-----------------------------------------------------------------------
/* (non-Javadoc)
* @see org.apache.commons.lang3.time.DateParser#getPattern()
*/
@Override
public String getPattern() {
return pattern;
}
/* (non-Javadoc)
* @see org.apache.commons.lang3.time.DateParser#getTimeZone()
*/
@Override
public TimeZone getTimeZone() {
return timeZone;
}
/* (non-Javadoc)
* @see org.apache.commons.lang3.time.DateParser#getLocale()
*/
@Override
public Locale getLocale() {
return locale;
}
/**
* Returns the generated pattern (for testing purposes).
*
* @return the generated pattern
*/
Pattern getParsePattern() {
return parsePattern;
}
// Basics
//-----------------------------------------------------------------------
/**
* <p>Compare another object for equality with this object.</p>
*
* @param obj the object to compare to
* @return <code>true</code>if equal to this instance
*/
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof FastDateParser)) {
return false;
}
final FastDateParser other = (FastDateParser) obj;
return pattern.equals(other.pattern)
&& timeZone.equals(other.timeZone)
&& locale.equals(other.locale);
}
/**
* <p>Return a hashcode compatible with equals.</p>
*
* @return a hashcode compatible with equals
*/
@Override
public int hashCode() {
return pattern.hashCode() + 13 * (timeZone.hashCode() + 13 * locale.hashCode());
}
/**
* <p>Get a string version of this formatter.</p>
*
* @return a debugging string
*/
@Override
public String toString() {
return "FastDateParser[" + pattern + "," + locale + "," + timeZone.getID() + "]";
}
// Serializing
//-----------------------------------------------------------------------
/**
* Create the object after serialization. This implementation reinitializes the
* transient properties.
*
* @param in ObjectInputStream from which the object is being deserialized.
* @throws IOException if there is an IO issue.
* @throws ClassNotFoundException if a class cannot be found.
*/
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
final Calendar definingCalendar = Calendar.getInstance(timeZone, locale);
init(definingCalendar);
}
/* (non-Javadoc)
* @see org.apache.commons.lang3.time.DateParser#parseObject(java.lang.String)
*/
@Override
public Object parseObject(final String source) throws ParseException {
return parse(source);
}
/* (non-Javadoc)
* @see org.apache.commons.lang3.time.DateParser#parse(java.lang.String)
*/
@Override
public Date parse(final String source) throws ParseException {
final Date date = parse(source, new ParsePosition(0));
if (date == null) {
// Add a note re supported date range
if (locale.equals(JAPANESE_IMPERIAL)) {
throw new ParseException(
"(The " + locale + " locale does not support dates before 1868 AD)\n" +
"Unparseable date: \"" + source + "\" does not match " + parsePattern.pattern(), 0);
}
throw new ParseException("Unparseable date: \"" + source + "\" does not match " + parsePattern.pattern(), 0);
}
return date;
}
/* (non-Javadoc)
* @see org.apache.commons.lang3.time.DateParser#parseObject(java.lang.String, java.text.ParsePosition)
*/
@Override
public Object parseObject(final String source, final ParsePosition pos) {
return parse(source, pos);
}
/* (non-Javadoc)
* @see org.apache.commons.lang3.time.DateParser#parse(java.lang.String, java.text.ParsePosition)
*/
@Override
public Date parse(final String source, final ParsePosition pos) {
final int offset = pos.getIndex();
final Matcher matcher = parsePattern.matcher(source.substring(offset));
if (!matcher.lookingAt()) {
return null;
}
// timing tests indicate getting new instance is 19% faster than cloning
final Calendar cal = Calendar.getInstance(timeZone, locale);
cal.clear();
for (int i = 0; i < strategies.length; ) {
final Strategy strategy = strategies[i++];
strategy.setCalendar(this, cal, matcher.group(i));
}
pos.setIndex(offset + matcher.end());
return cal.getTime();
}
// Support for strategies
//-----------------------------------------------------------------------
/**
* Escape constant fields into regular expression
*
* @param regex The destination regex
* @param value The source field
* @param unquote If true, replace two success quotes ('') with single quote (')
* @return The <code>StringBuilder</code>
*/
private static StringBuilder escapeRegex(final StringBuilder regex, final String value, final boolean unquote) {
regex.append("\\Q");
for (int i = 0; i < value.length(); ++i) {
char c = value.charAt(i);
switch (c) {
case '\'':
if (unquote) {
if (++i == value.length()) {
return regex;
}
c = value.charAt(i);
}
break;
case '\\':
if (++i == value.length()) {
break;
}
/*
* If we have found \E, we replace it with \E\\E\Q, i.e. we stop the quoting,
* quote the \ in \E, then restart the quoting.
*
* Otherwise we just output the two characters.
* In each case the initial \ needs to be output and the final char is done at the end
*/
regex.append(c); // we always want the original \
c = value.charAt(i); // Is it followed by E ?
if (c == 'E') { // \E detected
regex.append("E\\\\E\\"); // see comment above
c = 'Q'; // appended below
}
break;
default:
break;
}
regex.append(c);
}
regex.append("\\E");
return regex;
}
private static String[] getDisplayNameArray(int field, boolean isLong, Locale locale) {
DateFormatSymbols dfs = new DateFormatSymbols(locale);
switch (field) {
case Calendar.AM_PM:
return dfs.getAmPmStrings();
case Calendar.DAY_OF_WEEK:
return isLong ? dfs.getWeekdays() : dfs.getShortWeekdays();
case Calendar.ERA:
return dfs.getEras();
case Calendar.MONTH:
return isLong ? dfs.getMonths() : dfs.getShortMonths();
}
return null;
}
private static void insertValuesInMap(Map<String, Integer> map, String[] values) {
if (values == null) {
return;
}
for (int i = 0; i < values.length; ++i) {
if (values[i] != null && values[i].length() > 0) {
map.put(values[i], i);
}
}
}
private static Map<String, Integer> getDisplayNames(int field, Locale locale) {
Map<String, Integer> result = new HashMap<String, Integer>();
insertValuesInMap(result, getDisplayNameArray(field, false, locale));
insertValuesInMap(result, getDisplayNameArray(field, true, locale));
return result.isEmpty() ? null : result;
}
/**
* Get the short and long values displayed for a field
*
* @param field The field of interest
* @param definingCalendar The calendar to obtain the short and long values
* @param locale The locale of display names
* @return A Map of the field key / value pairs
*/
private static Map<String, Integer> getDisplayNames(final int field, final Calendar definingCalendar, final Locale locale) {
return getDisplayNames(field, locale);
}
/**
* Adjust dates to be within appropriate century
*
* @param twoDigitYear The year to adjust
* @return A value between centuryStart(inclusive) to centuryStart+100(exclusive)
*/
private int adjustYear(final int twoDigitYear) {
int trial = century + twoDigitYear;
return twoDigitYear >= startYear ? trial : trial + 100;
}
/**
* Is the next field a number?
*
* @return true, if next field will be a number
*/
boolean isNextNumber() {
return nextStrategy != null && nextStrategy.isNumber();
}
/**
* What is the width of the current field?
*
* @return The number of characters in the current format field
*/
int getFieldWidth() {
return currentFormatField.length();
}
/**
* A strategy to parse a single field from the parsing pattern
*/
private static abstract class Strategy {
/**
* Is this field a number?
* The default implementation returns false.
*
* @return true, if field is a number
*/
boolean isNumber() {
return false;
}
/**
* Set the Calendar with the parsed field.
* <p/>
* The default implementation does nothing.
*
* @param parser The parser calling this strategy
* @param cal The <code>Calendar</code> to set
* @param value The parsed field to translate and set in cal
*/
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
}
/**
* Generate a <code>Pattern</code> regular expression to the <code>StringBuilder</code>
* which will accept this field
*
* @param parser The parser calling this strategy
* @param regex The <code>StringBuilder</code> to append to
* @return true, if this field will set the calendar;
* false, if this field is a constant value
*/
abstract boolean addRegex(FastDateParser parser, StringBuilder regex);
}
/**
* A <code>Pattern</code> to parse the user supplied SimpleDateFormat pattern
*/
private static final Pattern formatPattern = Pattern.compile(
"D+|E+|F+|G+|H+|K+|M+|S+|W+|Z+|a+|d+|h+|k+|m+|s+|w+|y+|z+|''|'[^']++(''[^']*+)*+'|[^'A-Za-z]++");
/**
* Obtain a Strategy given a field from a SimpleDateFormat pattern
*
* @param formatField A sub-sequence of the SimpleDateFormat pattern
* @param definingCalendar The calendar to obtain the short and long values
* @return The Strategy that will handle parsing for the field
*/
private Strategy getStrategy(final String formatField, final Calendar definingCalendar) {
switch (formatField.charAt(0)) {
case '\'':
if (formatField.length() > 2) {
return new CopyQuotedStrategy(formatField.substring(1, formatField.length() - 1));
}
//$FALL-THROUGH$
default:
return new CopyQuotedStrategy(formatField);
case 'D':
return DAY_OF_YEAR_STRATEGY;
case 'E':
return getLocaleSpecificStrategy(Calendar.DAY_OF_WEEK, definingCalendar);
case 'F':
return DAY_OF_WEEK_IN_MONTH_STRATEGY;
case 'G':
return getLocaleSpecificStrategy(Calendar.ERA, definingCalendar);
case 'H':
return MODULO_HOUR_OF_DAY_STRATEGY;
case 'K':
return HOUR_STRATEGY;
case 'M':
return formatField.length() >= 3 ? getLocaleSpecificStrategy(Calendar.MONTH, definingCalendar) : NUMBER_MONTH_STRATEGY;
case 'S':
return MILLISECOND_STRATEGY;
case 'W':
return WEEK_OF_MONTH_STRATEGY;
case 'a':
return getLocaleSpecificStrategy(Calendar.AM_PM, definingCalendar);
case 'd':
return DAY_OF_MONTH_STRATEGY;
case 'h':
return MODULO_HOUR_STRATEGY;
case 'k':
return HOUR_OF_DAY_STRATEGY;
case 'm':
return MINUTE_STRATEGY;
case 's':
return SECOND_STRATEGY;
case 'w':
return WEEK_OF_YEAR_STRATEGY;
case 'y':
return formatField.length() > 2 ? LITERAL_YEAR_STRATEGY : ABBREVIATED_YEAR_STRATEGY;
case 'Z':
case 'z':
return getLocaleSpecificStrategy(Calendar.ZONE_OFFSET, definingCalendar);
}
}
@SuppressWarnings("unchecked") // OK because we are creating an array with no entries
private static final ConcurrentMap<Locale, Strategy>[] caches = new ConcurrentMap[Calendar.FIELD_COUNT];
/**
* Get a cache of Strategies for a particular field
*
* @param field The Calendar field
* @return a cache of Locale to Strategy
*/
private static ConcurrentMap<Locale, Strategy> getCache(final int field) {
synchronized (caches) {
if (caches[field] == null) {
caches[field] = new ConcurrentHashMap<Locale, Strategy>(3);
}
return caches[field];
}
}
/**
* Construct a Strategy that parses a Text field
*
* @param field The Calendar field
* @param definingCalendar The calendar to obtain the short and long values
* @return a TextStrategy for the field and Locale
*/
private Strategy getLocaleSpecificStrategy(final int field, final Calendar definingCalendar) {
final ConcurrentMap<Locale, Strategy> cache = getCache(field);
Strategy strategy = cache.get(locale);
if (strategy == null) {
strategy = field == Calendar.ZONE_OFFSET
? new TimeZoneStrategy(locale)
: new TextStrategy(field, definingCalendar, locale);
final Strategy inCache = cache.putIfAbsent(locale, strategy);
if (inCache != null) {
return inCache;
}
}
return strategy;
}
/**
* A strategy that copies the static or quoted field in the parsing pattern
*/
private static class CopyQuotedStrategy extends Strategy {
private final String formatField;
/**
* Construct a Strategy that ensures the formatField has literal text
*
* @param formatField The literal text to match
*/
CopyQuotedStrategy(final String formatField) {
this.formatField = formatField;
}
/**
* {@inheritDoc}
*/
@Override
boolean isNumber() {
char c = formatField.charAt(0);
if (c == '\'') {
c = formatField.charAt(1);
}
return Character.isDigit(c);
}
/**
* {@inheritDoc}
*/
@Override
boolean addRegex(final FastDateParser parser, final StringBuilder regex) {
escapeRegex(regex, formatField, true);
return false;
}
}
/**
* A strategy that handles a text field in the parsing pattern
*/
private static class TextStrategy extends Strategy {
private final int field;
private final Map<String, Integer> keyValues;
/**
* Construct a Strategy that parses a Text field
*
* @param field The Calendar field
* @param definingCalendar The Calendar to use
* @param locale The Locale to use
*/
TextStrategy(final int field, final Calendar definingCalendar, final Locale locale) {
this.field = field;
this.keyValues = getDisplayNames(field, definingCalendar, locale);
}
/**
* {@inheritDoc}
*/
@Override
boolean addRegex(final FastDateParser parser, final StringBuilder regex) {
regex.append('(');
for (final String textKeyValue : keyValues.keySet()) {
escapeRegex(regex, textKeyValue, false).append('|');
}
regex.setCharAt(regex.length() - 1, ')');
return true;
}
/**
* {@inheritDoc}
*/
@Override
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
final Integer iVal = keyValues.get(value);
if (iVal == null) {
final StringBuilder sb = new StringBuilder(value);
sb.append(" not in (");
for (final String textKeyValue : keyValues.keySet()) {
sb.append(textKeyValue).append(' ');
}
sb.setCharAt(sb.length() - 1, ')');
throw new IllegalArgumentException(sb.toString());
}
cal.set(field, iVal.intValue());
}
}
/**
* A strategy that handles a number field in the parsing pattern
*/
private static class NumberStrategy extends Strategy {
private final int field;
/**
* Construct a Strategy that parses a Number field
*
* @param field The Calendar field
*/
NumberStrategy(final int field) {
this.field = field;
}
/**
* {@inheritDoc}
*/
@Override
boolean isNumber() {
return true;
}
/**
* {@inheritDoc}
*/
@Override
boolean addRegex(final FastDateParser parser, final StringBuilder regex) {
// See LANG-954: We use {Nd} rather than {IsNd} because Android does not support the Is prefix
if (parser.isNextNumber()) {
regex.append("(\\p{Nd}{").append(parser.getFieldWidth()).append("}+)");
} else {
regex.append("(\\p{Nd}++)");
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
cal.set(field, modify(Integer.parseInt(value)));
}
/**
* Make any modifications to parsed integer
*
* @param iValue The parsed integer
* @return The modified value
*/
int modify(final int iValue) {
return iValue;
}
}
private static final Strategy ABBREVIATED_YEAR_STRATEGY = new NumberStrategy(Calendar.YEAR) {
/**
* {@inheritDoc}
*/
@Override
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
int iValue = Integer.parseInt(value);
if (iValue < 100) {
iValue = parser.adjustYear(iValue);
}
cal.set(Calendar.YEAR, iValue);
}
};
/**
* A strategy that handles a timezone field in the parsing pattern
*/
private static class TimeZoneStrategy extends Strategy {
private final String validTimeZoneChars;
private final SortedMap<String, TimeZone> tzNames = new TreeMap<String, TimeZone>(String.CASE_INSENSITIVE_ORDER);
/**
* Index of zone id
*/
private static final int ID = 0;
/**
* Index of the long name of zone in standard time
*/
private static final int LONG_STD = 1;
/**
* Index of the short name of zone in standard time
*/
private static final int SHORT_STD = 2;
/**
* Index of the long name of zone in daylight saving time
*/
private static final int LONG_DST = 3;
/**
* Index of the short name of zone in daylight saving time
*/
private static final int SHORT_DST = 4;
/**
* Construct a Strategy that parses a TimeZone
*
* @param locale The Locale
*/
TimeZoneStrategy(final Locale locale) {
final String[][] zones = DateFormatSymbols.getInstance(locale).getZoneStrings();
for (String[] zone : zones) {
if (zone[ID].startsWith("GMT")) {
continue;
}
final TimeZone tz = TimeZone.getTimeZone(zone[ID]);
if (!tzNames.containsKey(zone[LONG_STD])) {
tzNames.put(zone[LONG_STD], tz);
}
if (!tzNames.containsKey(zone[SHORT_STD])) {
tzNames.put(zone[SHORT_STD], tz);
}
if (tz.useDaylightTime()) {
if (!tzNames.containsKey(zone[LONG_DST])) {
tzNames.put(zone[LONG_DST], tz);
}
if (!tzNames.containsKey(zone[SHORT_DST])) {
tzNames.put(zone[SHORT_DST], tz);
}
}
}
final StringBuilder sb = new StringBuilder();
sb.append("(GMT[+\\-]\\d{0,1}\\d{2}|[+\\-]\\d{2}:?\\d{2}|");
for (final String id : tzNames.keySet()) {
escapeRegex(sb, id, false).append('|');
}
sb.setCharAt(sb.length() - 1, ')');
validTimeZoneChars = sb.toString();
}
/**
* {@inheritDoc}
*/
@Override
boolean addRegex(final FastDateParser parser, final StringBuilder regex) {
regex.append(validTimeZoneChars);
return true;
}
/**
* {@inheritDoc}
*/
@Override
void setCalendar(final FastDateParser parser, final Calendar cal, final String value) {
TimeZone tz;
if (value.charAt(0) == '+' || value.charAt(0) == '-') {
tz = TimeZone.getTimeZone("GMT" + value);
} else if (value.startsWith("GMT")) {
tz = TimeZone.getTimeZone(value);
} else {
tz = tzNames.get(value);
if (tz == null) {
throw new IllegalArgumentException(value + " is not a supported timezone name");
}
}
cal.setTimeZone(tz);
}
}
private static final Strategy NUMBER_MONTH_STRATEGY = new NumberStrategy(Calendar.MONTH) {
@Override
int modify(final int iValue) {
return iValue - 1;
}
};
private static final Strategy LITERAL_YEAR_STRATEGY = new NumberStrategy(Calendar.YEAR);
private static final Strategy WEEK_OF_YEAR_STRATEGY = new NumberStrategy(Calendar.WEEK_OF_YEAR);
private static final Strategy WEEK_OF_MONTH_STRATEGY = new NumberStrategy(Calendar.WEEK_OF_MONTH);
private static final Strategy DAY_OF_YEAR_STRATEGY = new NumberStrategy(Calendar.DAY_OF_YEAR);
private static final Strategy DAY_OF_MONTH_STRATEGY = new NumberStrategy(Calendar.DAY_OF_MONTH);
private static final Strategy DAY_OF_WEEK_IN_MONTH_STRATEGY = new NumberStrategy(Calendar.DAY_OF_WEEK_IN_MONTH);
private static final Strategy HOUR_OF_DAY_STRATEGY = new NumberStrategy(Calendar.HOUR_OF_DAY);
private static final Strategy MODULO_HOUR_OF_DAY_STRATEGY = new NumberStrategy(Calendar.HOUR_OF_DAY) {
@Override
int modify(final int iValue) {
return iValue % 24;
}
};
private static final Strategy MODULO_HOUR_STRATEGY = new NumberStrategy(Calendar.HOUR) {
@Override
int modify(final int iValue) {
return iValue % 12;
}
};
private static final Strategy HOUR_STRATEGY = new NumberStrategy(Calendar.HOUR);
private static final Strategy MINUTE_STRATEGY = new NumberStrategy(Calendar.MINUTE);
private static final Strategy SECOND_STRATEGY = new NumberStrategy(Calendar.SECOND);
private static final Strategy MILLISECOND_STRATEGY = new NumberStrategy(Calendar.MILLISECOND);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,265 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.android.time;
import java.text.DateFormat;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* <p>FormatCache is a cache and factory for {@link Format}s.</p>
*
* @since 3.0
* @version $Id: FormatCache 892161 2009-12-18 07:21:10Z $
*/
// TODO: Before making public move from getDateTimeInstance(Integer,...) to int; or some other approach.
abstract class FormatCache<F extends Format> {
/**
* No date or no time. Used in same parameters as DateFormat.SHORT or DateFormat.LONG
*/
static final int NONE = -1;
private final ConcurrentMap<MultipartKey, F> cInstanceCache
= new ConcurrentHashMap<MultipartKey, F>(7);
private static final ConcurrentMap<MultipartKey, String> cDateTimeInstanceCache
= new ConcurrentHashMap<MultipartKey, String>(7);
/**
* <p>Gets a formatter instance using the default pattern in the
* default timezone and locale.</p>
*
* @return a date/time formatter
*/
public F getInstance() {
return getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, TimeZone.getDefault(), Locale.getDefault());
}
/**
* <p>Gets a formatter instance using the specified pattern, time zone
* and locale.</p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible
* pattern, non-null
* @param timeZone the time zone, null means use the default TimeZone
* @param locale the locale, null means use the default Locale
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
* or <code>null</code>
*/
public F getInstance(final String pattern, TimeZone timeZone, Locale locale) {
if (pattern == null) {
throw new NullPointerException("pattern must not be null");
}
if (timeZone == null) {
timeZone = TimeZone.getDefault();
}
if (locale == null) {
locale = Locale.getDefault();
}
final MultipartKey key = new MultipartKey(pattern, timeZone, locale);
F format = cInstanceCache.get(key);
if (format == null) {
format = createInstance(pattern, timeZone, locale);
final F previousValue = cInstanceCache.putIfAbsent(key, format);
if (previousValue != null) {
// another thread snuck in and did the same work
// we should return the instance that is in ConcurrentMap
format = previousValue;
}
}
return format;
}
/**
* <p>Create a format instance using the specified pattern, time zone
* and locale.</p>
*
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern, this will not be null.
* @param timeZone time zone, this will not be null.
* @param locale locale, this will not be null.
* @return a pattern based date/time formatter
* @throws IllegalArgumentException if pattern is invalid
* or <code>null</code>
*/
abstract protected F createInstance(String pattern, TimeZone timeZone, Locale locale);
/**
* <p>Gets a date/time formatter instance using the specified style,
* time zone and locale.</p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT, null indicates no date in format
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no time in format
* @param timeZone optional time zone, overrides time zone of
* formatted date, null means use default Locale
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time
* pattern defined
*/
// This must remain private, see LANG-884
private F getDateTimeInstance(final Integer dateStyle, final Integer timeStyle, final TimeZone timeZone, Locale locale) {
if (locale == null) {
locale = Locale.getDefault();
}
final String pattern = getPatternForStyle(dateStyle, timeStyle, locale);
return getInstance(pattern, timeZone, locale);
}
/**
* <p>Gets a date/time formatter instance using the specified style,
* time zone and locale.</p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted date, null means use default Locale
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time
* pattern defined
*/
// package protected, for access from FastDateFormat; do not make public or protected
F getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone, Locale locale) {
return getDateTimeInstance(Integer.valueOf(dateStyle), Integer.valueOf(timeStyle), timeZone, locale);
}
/**
* <p>Gets a date formatter instance using the specified style,
* time zone and locale.</p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted date, null means use default Locale
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time
* pattern defined
*/
// package protected, for access from FastDateFormat; do not make public or protected
F getDateInstance(final int dateStyle, final TimeZone timeZone, Locale locale) {
return getDateTimeInstance(Integer.valueOf(dateStyle), null, timeZone, locale);
}
/**
* <p>Gets a time formatter instance using the specified style,
* time zone and locale.</p>
*
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
* @param timeZone optional time zone, overrides time zone of
* formatted date, null means use default Locale
* @param locale optional locale, overrides system locale
* @return a localized standard date/time formatter
* @throws IllegalArgumentException if the Locale has no date/time
* pattern defined
*/
// package protected, for access from FastDateFormat; do not make public or protected
F getTimeInstance(final int timeStyle, final TimeZone timeZone, Locale locale) {
return getDateTimeInstance(null, Integer.valueOf(timeStyle), timeZone, locale);
}
/**
* <p>Gets a date/time format for the specified styles and locale.</p>
*
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT, null indicates no date in format
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no time in format
* @param locale The non-null locale of the desired format
* @return a localized standard date/time format
* @throws IllegalArgumentException if the Locale has no date/time pattern defined
*/
// package protected, for access from test code; do not make public or protected
static String getPatternForStyle(final Integer dateStyle, final Integer timeStyle, final Locale locale) {
final MultipartKey key = new MultipartKey(dateStyle, timeStyle, locale);
String pattern = cDateTimeInstanceCache.get(key);
if (pattern == null) {
try {
DateFormat formatter;
if (dateStyle == null) {
formatter = DateFormat.getTimeInstance(timeStyle.intValue(), locale);
} else if (timeStyle == null) {
formatter = DateFormat.getDateInstance(dateStyle.intValue(), locale);
} else {
formatter = DateFormat.getDateTimeInstance(dateStyle.intValue(), timeStyle.intValue(), locale);
}
pattern = ((SimpleDateFormat) formatter).toPattern();
final String previous = cDateTimeInstanceCache.putIfAbsent(key, pattern);
if (previous != null) {
// even though it doesn't matter if another thread put the pattern
// it's still good practice to return the String instance that is
// actually in the ConcurrentMap
pattern = previous;
}
} catch (final ClassCastException ex) {
throw new IllegalArgumentException("No date time pattern for locale: " + locale);
}
}
return pattern;
}
// ----------------------------------------------------------------------
/**
* <p>Helper class to hold multi-part Map keys</p>
*/
private static class MultipartKey {
private final Object[] keys;
private int hashCode;
/**
* Constructs an instance of <code>MultipartKey</code> to hold the specified objects.
*
* @param keys the set of objects that make up the key. Each key may be null.
*/
public MultipartKey(final Object... keys) {
this.keys = keys;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(final Object obj) {
// Eliminate the usual boilerplate because
// this inner static class is only used in a generic ConcurrentHashMap
// which will not compare against other Object types
return Arrays.equals(keys, ((MultipartKey) obj).keys);
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
if (hashCode == 0) {
int rc = 0;
for (final Object key : keys) {
if (key != null) {
rc = rc * 7 + key.hashCode();
}
}
hashCode = rc;
}
return hashCode;
}
}
}

View File

@ -16,7 +16,7 @@ import com.coremedia.iso.boxes.AbstractMediaHeaderBox;
import com.coremedia.iso.boxes.SampleDescriptionBox;
import com.coremedia.iso.boxes.SoundMediaHeaderBox;
import com.coremedia.iso.boxes.VideoMediaHeaderBox;
import com.coremedia.iso.boxes.h264.AvcConfigurationBox;
import com.mp4parser.iso14496.part15.AvcConfigurationBox;
import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
import com.coremedia.iso.boxes.sampleentry.VisualSampleEntry;
import com.googlecode.mp4parser.boxes.mp4.ESDescriptorBox;

View File

@ -6,7 +6,7 @@
* Copyright Nikolai Kudashov, 2013.
*/
package org.telegram.ui;
package org.telegram.messenger;
import android.app.AlarmManager;
import android.app.Application;
@ -21,6 +21,7 @@ import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
import android.os.PowerManager;
@ -33,15 +34,10 @@ import org.telegram.android.ContactsController;
import org.telegram.android.MediaController;
import org.telegram.android.NotificationsService;
import org.telegram.android.SendMessagesHelper;
import org.telegram.messenger.BuildVars;
import org.telegram.messenger.ConnectionsManager;
import org.telegram.messenger.FileLog;
import org.telegram.android.LocaleController;
import org.telegram.android.MessagesController;
import org.telegram.android.NativeLoader;
import org.telegram.android.ScreenReceiver;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.Utilities;
import java.util.concurrent.atomic.AtomicInteger;
@ -112,14 +108,17 @@ public class ApplicationLoader extends Application {
@Override
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);
applicationHandler = new Handler(applicationContext.getMainLooper());
java.lang.System.setProperty("java.net.preferIPv4Stack", "true");
java.lang.System.setProperty("java.net.preferIPv6Addresses", "false");
startPushService();
}
@ -268,7 +267,7 @@ public class ApplicationLoader extends Application {
UserConfig.registeredForPush = !isNew;
UserConfig.saveConfig(false);
if (UserConfig.getClientUserId() != 0) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
MessagesController.getInstance().registerForPush(regid);

View File

@ -14,7 +14,6 @@ import android.content.pm.PackageInfo;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.PowerManager;
import android.util.Base64;
import org.telegram.android.AndroidUtilities;
@ -22,7 +21,6 @@ import org.telegram.android.ContactsController;
import org.telegram.android.LocaleController;
import org.telegram.android.MessagesController;
import org.telegram.android.NotificationCenter;
import org.telegram.ui.ApplicationLoader;
import java.io.File;
import java.util.ArrayList;
@ -83,8 +81,6 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
private volatile long nextCallToken = 1;
private PowerManager.WakeLock wakeLock = null;
private static volatile ConnectionsManager Instance = null;
public static ConnectionsManager getInstance() {
ConnectionsManager localInstance = Instance;
@ -217,10 +213,6 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
}
Utilities.stageQueue.postRunnable(stageRunnable, 1000);
PowerManager pm = (PowerManager)ApplicationLoader.applicationContext.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "lock");
wakeLock.setReferenceCounted(false);
}
public int getConnectionState() {
@ -425,7 +417,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
pushSessionId = Utilities.random.nextLong();
}
if (currentDatacenterId == 0) {
currentDatacenterId = 1;
currentDatacenterId = 2;
}
saveSession();
}
@ -439,12 +431,12 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
if (isTestBackend == 0) {
Datacenter datacenter = new Datacenter();
datacenter.datacenterId = 1;
datacenter.addAddressAndPort("173.240.5.1", 443);
datacenter.addAddressAndPort("149.154.175.50", 443);
datacenters.put(datacenter.datacenterId, datacenter);
datacenter = new Datacenter();
datacenter.datacenterId = 2;
datacenter.addAddressAndPort("149.154.167.50", 443);
datacenter.addAddressAndPort("149.154.167.51", 443);
datacenters.put(datacenter.datacenterId, datacenter);
datacenter = new Datacenter();
@ -454,7 +446,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
datacenter = new Datacenter();
datacenter.datacenterId = 4;
datacenter.addAddressAndPort("149.154.167.90", 443);
datacenter.addAddressAndPort("149.154.167.91", 443);
datacenters.put(datacenter.datacenterId, datacenter);
datacenter = new Datacenter();
@ -480,7 +472,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
} else if (datacenters.size() == 1) {
Datacenter datacenter = new Datacenter();
datacenter.datacenterId = 2;
datacenter.addAddressAndPort("149.154.167.50", 443);
datacenter.addAddressAndPort("149.154.167.51", 443);
datacenters.put(datacenter.datacenterId, datacenter);
datacenter = new Datacenter();
@ -490,12 +482,12 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
datacenter = new Datacenter();
datacenter.datacenterId = 4;
datacenter.addAddressAndPort("31.210.235.12", 443);
datacenter.addAddressAndPort("149.154.167.91", 443);
datacenters.put(datacenter.datacenterId, datacenter);
datacenter = new Datacenter();
datacenter.datacenterId = 5;
datacenter.addAddressAndPort("116.51.22.2", 443);
datacenter.addAddressAndPort("149.154.171.5", 443);
datacenters.put(datacenter.datacenterId, datacenter);
}
}
@ -646,7 +638,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
}
public void bindRequestToGuid(final Long request, final int guid) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
ArrayList<Long> requests = requestsByGuids.get(guid);
@ -659,7 +651,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
}
public void removeRequestInClass(final Long request) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
Integer guid = requestsByClass.get(request);
@ -831,12 +823,11 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
if (invoke.system_version == null || invoke.system_version.length() == 0) {
invoke.system_version = "SDK Unknown";
}
object = invoke;
}
TLRPC.invokeWithLayer18 invoke = new TLRPC.invokeWithLayer18();
invoke.query = object;
TLRPC.invokeWithLayer invoke2 = new TLRPC.invokeWithLayer();
invoke2.query = invoke;
FileLog.d("wrap in layer", "" + object);
return invoke;
object = invoke2;
}
}
return object;
}
@ -1121,13 +1112,13 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
request.retryCount++;
if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) {
if (!request.salt && (request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) {
int retryMax = 10;
if ((request.flags & RPCRequest.RPCRequestClassForceDownload) == 0) {
if (request.wait) {
retryMax = 1;
} else {
retryMax = 3;
retryMax = 6;
}
}
if (request.retryCount >= retryMax) {
@ -1614,12 +1605,12 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
TLRPC.TL_protoMessage message = networkMessage.protoMessage;
if (BuildVars.DEBUG_VERSION) {
if (message.body instanceof TLRPC.invokeWithLayer18) {
FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer18)message.body).query);
if (message.body instanceof TLRPC.invokeWithLayer) {
FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer)message.body).query);
} else if (message.body instanceof TLRPC.initConnection) {
TLRPC.initConnection r = (TLRPC.initConnection)message.body;
if (r.query instanceof TLRPC.invokeWithLayer18) {
FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer18)r.query).query);
if (r.query instanceof TLRPC.invokeWithLayer) {
FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer)r.query).query);
} else {
FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + r.query);
}
@ -1654,12 +1645,12 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
TLRPC.TL_protoMessage message = networkMessage.protoMessage;
containerMessages.add(message);
if (BuildVars.DEBUG_VERSION) {
if (message.body instanceof TLRPC.invokeWithLayer18) {
FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer18)message.body).query);
if (message.body instanceof TLRPC.invokeWithLayer) {
FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer)message.body).query);
} else if (message.body instanceof TLRPC.initConnection) {
TLRPC.initConnection r = (TLRPC.initConnection)message.body;
if (r.query instanceof TLRPC.invokeWithLayer18) {
FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer18)r.query).query);
if (r.query instanceof TLRPC.invokeWithLayer) {
FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer)r.query).query);
} else {
FileLog.d("tmessages", connection.getSissionId() + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + r.query);
}
@ -2139,14 +2130,6 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
} else {
if (resultContainer.result instanceof TLRPC.updates_Difference) {
pushMessagesReceived = true;
AndroidUtilities.RunOnUIThread(new Runnable() {
@Override
public void run() {
if (wakeLock.isHeld()) {
wakeLock.release();
}
}
});
}
request.completionBlock.run(resultContainer.result, null);
}
@ -2158,7 +2141,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) {
if (UserConfig.isClientActivated()) {
UserConfig.clearConfig();
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.appDidLogout);
@ -2252,6 +2235,19 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
lastOutgoingMessageId = Math.max(messageId, lastOutgoingMessageId);
}
long resultMid = ((TLRPC.TL_bad_server_salt) message).bad_msg_id;
if (resultMid != 0) {
for (RPCRequest request : runningRequests) {
if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) == 0) {
continue;
}
if (request.respondsToMessageId(resultMid)) {
request.retryCount = 0;
request.salt = true;
break;
}
}
}
datacenter.clearServerSalts();
@ -2317,23 +2313,9 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
if (paused) {
pushMessagesReceived = false;
}
AndroidUtilities.RunOnUIThread(new Runnable() {
@Override
public void run() {
wakeLock.acquire(20000);
}
});
resumeNetworkInternal();
} else {
pushMessagesReceived = true;
AndroidUtilities.RunOnUIThread(new Runnable() {
@Override
public void run() {
if (wakeLock.isHeld()) {
wakeLock.release();
}
}
});
MessagesController.getInstance().processUpdates((TLRPC.Updates) message, false);
}
} else {
@ -2431,7 +2413,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
}
}
final int stateCopy = connectionState;
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.didUpdatedConnectionState, stateCopy);
@ -2481,7 +2463,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
if (ConnectionsManager.getInstance().connectionState == 3 && !MessagesController.getInstance().gettingDifference && !MessagesController.getInstance().gettingDifferenceAgain) {
ConnectionsManager.getInstance().connectionState = 0;
final int stateCopy = ConnectionsManager.getInstance().connectionState;
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.didUpdatedConnectionState, stateCopy);
@ -2497,7 +2479,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
if (connectionState == 1 || connectionState == 2) {
connectionState = 3;
final int stateCopy = connectionState;
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
NotificationCenter.getInstance().postNotificationName(NotificationCenter.didUpdatedConnectionState, stateCopy);
@ -2587,7 +2569,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
if (message == null) {
FileLog.e("tmessages", "***** Error parsing message: " + constructor);
} else {
FileLog.e("tmessages", "received object " + message);
FileLog.d("tmessages", "received object " + message);
processMessage(message, messageId, messageSeqNo, messageServerSalt, connection, 0, 0);
connection.addProcessedMessageId(messageId);

View File

@ -11,8 +11,6 @@ package org.telegram.messenger;
import android.content.Context;
import android.content.SharedPreferences;
import org.telegram.ui.ApplicationLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

View File

@ -11,8 +11,7 @@ package org.telegram.messenger;
import android.net.Uri;
import android.util.Log;
import org.telegram.android.FastDateFormat;
import org.telegram.ui.ApplicationLoader;
import org.telegram.android.time.FastDateFormat;
import java.io.File;
import java.io.FileOutputStream;

View File

@ -11,8 +11,6 @@ package org.telegram.messenger;
import android.app.Activity;
import android.content.SharedPreferences;
import org.telegram.ui.ApplicationLoader;
import java.io.File;
import java.io.FileInputStream;
import java.math.BigInteger;

View File

@ -37,6 +37,7 @@ public class RPCRequest {
int serverFailureCount;
int flags;
boolean wait = false;
boolean salt = false;
protected int retryCount = 0;
protected int lastResendTime = 0;
protected boolean completed = false;

View File

@ -51,6 +51,8 @@ public class TLClassStore {
classStore.put(TLRPC.TL_updates_difference.constructor, TLRPC.TL_updates_difference.class);
classStore.put(TLRPC.TL_geoPointEmpty.constructor, TLRPC.TL_geoPointEmpty.class);
classStore.put(TLRPC.TL_geoPoint.constructor, TLRPC.TL_geoPoint.class);
classStore.put(TLRPC.TL_privacyKeyStatusTimestamp.constructor, TLRPC.TL_privacyKeyStatusTimestamp.class);
classStore.put(TLRPC.TL_account_privacyRules.constructor, TLRPC.TL_account_privacyRules.class);
classStore.put(TLRPC.TL_help_appUpdate.constructor, TLRPC.TL_help_appUpdate.class);
classStore.put(TLRPC.TL_help_noAppUpdate.constructor, TLRPC.TL_help_noAppUpdate.class);
classStore.put(TLRPC.TL_messageForwarded.constructor, TLRPC.TL_messageForwarded.class);
@ -93,8 +95,15 @@ public class TLClassStore {
classStore.put(TLRPC.TL_audioEmpty.constructor, TLRPC.TL_audioEmpty.class);
classStore.put(TLRPC.TL_audio.constructor, TLRPC.TL_audio.class);
classStore.put(TLRPC.TL_destroy_sessions_res.constructor, TLRPC.TL_destroy_sessions_res.class);
classStore.put(TLRPC.TL_privacyValueAllowUsers.constructor, TLRPC.TL_privacyValueAllowUsers.class);
classStore.put(TLRPC.TL_privacyValueDisallowAll.constructor, TLRPC.TL_privacyValueDisallowAll.class);
classStore.put(TLRPC.TL_privacyValueAllowContacts.constructor, TLRPC.TL_privacyValueAllowContacts.class);
classStore.put(TLRPC.TL_privacyValueDisallowContacts.constructor, TLRPC.TL_privacyValueDisallowContacts.class);
classStore.put(TLRPC.TL_privacyValueAllowAll.constructor, TLRPC.TL_privacyValueAllowAll.class);
classStore.put(TLRPC.TL_privacyValueDisallowUsers.constructor, TLRPC.TL_privacyValueDisallowUsers.class);
classStore.put(TLRPC.TL_contacts_contacts.constructor, TLRPC.TL_contacts_contacts.class);
classStore.put(TLRPC.TL_contacts_contactsNotModified.constructor, TLRPC.TL_contacts_contactsNotModified.class);
classStore.put(TLRPC.TL_inputPrivacyKeyStatusTimestamp.constructor, TLRPC.TL_inputPrivacyKeyStatusTimestamp.class);
classStore.put(TLRPC.TL_photos_photos.constructor, TLRPC.TL_photos_photos.class);
classStore.put(TLRPC.TL_photos_photosSlice.constructor, TLRPC.TL_photos_photosSlice.class);
classStore.put(TLRPC.TL_chatFull.constructor, TLRPC.TL_chatFull.class);
@ -165,6 +174,12 @@ public class TLClassStore {
classStore.put(TLRPC.TL_messages_affectedHistory.constructor, TLRPC.TL_messages_affectedHistory.class);
classStore.put(TLRPC.TL_documentEmpty.constructor, TLRPC.TL_documentEmpty.class);
classStore.put(TLRPC.TL_document.constructor, TLRPC.TL_document.class);
classStore.put(TLRPC.TL_inputPrivacyValueDisallowUsers.constructor, TLRPC.TL_inputPrivacyValueDisallowUsers.class);
classStore.put(TLRPC.TL_inputPrivacyValueDisallowAll.constructor, TLRPC.TL_inputPrivacyValueDisallowAll.class);
classStore.put(TLRPC.TL_inputPrivacyValueDisallowContacts.constructor, TLRPC.TL_inputPrivacyValueDisallowContacts.class);
classStore.put(TLRPC.TL_inputPrivacyValueAllowAll.constructor, TLRPC.TL_inputPrivacyValueAllowAll.class);
classStore.put(TLRPC.TL_inputPrivacyValueAllowContacts.constructor, TLRPC.TL_inputPrivacyValueAllowContacts.class);
classStore.put(TLRPC.TL_inputPrivacyValueAllowUsers.constructor, TLRPC.TL_inputPrivacyValueAllowUsers.class);
classStore.put(TLRPC.TL_inputMediaContact.constructor, TLRPC.TL_inputMediaContact.class);
classStore.put(TLRPC.TL_inputMediaUploadedThumbDocument.constructor, TLRPC.TL_inputMediaUploadedThumbDocument.class);
classStore.put(TLRPC.TL_inputMediaAudio.constructor, TLRPC.TL_inputMediaAudio.class);
@ -187,9 +202,12 @@ public class TLClassStore {
classStore.put(TLRPC.TL_contactSuggested.constructor, TLRPC.TL_contactSuggested.class);
classStore.put(TLRPC.TL_server_DH_params_fail.constructor, TLRPC.TL_server_DH_params_fail.class);
classStore.put(TLRPC.TL_server_DH_params_ok.constructor, TLRPC.TL_server_DH_params_ok.class);
classStore.put(TLRPC.TL_userStatusEmpty.constructor, TLRPC.TL_userStatusEmpty.class);
classStore.put(TLRPC.TL_userStatusOnline.constructor, TLRPC.TL_userStatusOnline.class);
classStore.put(TLRPC.TL_userStatusOffline.constructor, TLRPC.TL_userStatusOffline.class);
classStore.put(TLRPC.TL_userStatusLastWeek.constructor, TLRPC.TL_userStatusLastWeek.class);
classStore.put(TLRPC.TL_userStatusEmpty.constructor, TLRPC.TL_userStatusEmpty.class);
classStore.put(TLRPC.TL_userStatusLastMonth.constructor, TLRPC.TL_userStatusLastMonth.class);
classStore.put(TLRPC.TL_userStatusOnline.constructor, TLRPC.TL_userStatusOnline.class);
classStore.put(TLRPC.TL_userStatusRecently.constructor, TLRPC.TL_userStatusRecently.class);
classStore.put(TLRPC.TL_msg_copy.constructor, TLRPC.TL_msg_copy.class);
classStore.put(TLRPC.TL_contacts_importedContacts.constructor, TLRPC.TL_contacts_importedContacts.class);
classStore.put(TLRPC.TL_futureSalt.constructor, TLRPC.TL_futureSalt.class);
@ -210,6 +228,7 @@ public class TLClassStore {
classStore.put(TLRPC.TL_updateEncryptedChatTyping.constructor, TLRPC.TL_updateEncryptedChatTyping.class);
classStore.put(TLRPC.TL_updateDcOptions.constructor, TLRPC.TL_updateDcOptions.class);
classStore.put(TLRPC.TL_updateChatParticipants.constructor, TLRPC.TL_updateChatParticipants.class);
classStore.put(TLRPC.TL_updatePrivacy.constructor, TLRPC.TL_updatePrivacy.class);
classStore.put(TLRPC.TL_updateEncryption.constructor, TLRPC.TL_updateEncryption.class);
classStore.put(TLRPC.TL_updateUserBlocked.constructor, TLRPC.TL_updateUserBlocked.class);
classStore.put(TLRPC.TL_updateActivation.constructor, TLRPC.TL_updateActivation.class);
@ -328,6 +347,7 @@ public class TLClassStore {
classStore.put(TLRPC.TL_inputPhotoEmpty.constructor, TLRPC.TL_inputPhotoEmpty.class);
classStore.put(TLRPC.TL_inputPhoto.constructor, TLRPC.TL_inputPhoto.class);
classStore.put(TLRPC.TL_importedContact.constructor, TLRPC.TL_importedContact.class);
classStore.put(TLRPC.TL_accountDaysTTL.constructor, TLRPC.TL_accountDaysTTL.class);
classStore.put(TLRPC.TL_inputPeerContact.constructor, TLRPC.TL_inputPeerContact.class);
classStore.put(TLRPC.TL_inputPeerChat.constructor, TLRPC.TL_inputPeerChat.class);
classStore.put(TLRPC.TL_inputPeerEmpty.constructor, TLRPC.TL_inputPeerEmpty.class);
@ -339,6 +359,13 @@ public class TLClassStore {
classStore.put(TLRPC.TL_inputPhotoCrop.constructor, TLRPC.TL_inputPhotoCrop.class);
classStore.put(TLRPC.TL_messages_dialogs.constructor, TLRPC.TL_messages_dialogs.class);
classStore.put(TLRPC.TL_messages_dialogsSlice.constructor, TLRPC.TL_messages_dialogsSlice.class);
classStore.put(TLRPC.TL_account_sentChangePhoneCode.constructor, TLRPC.TL_account_sentChangePhoneCode.class);
classStore.put(TLRPC.TL_updateUserPhone.constructor, TLRPC.TL_updateUserPhone.class);
classStore.put(TLRPC.TL_decryptedMessageActionRequestKey.constructor, TLRPC.TL_decryptedMessageActionRequestKey.class);
classStore.put(TLRPC.TL_decryptedMessageActionAcceptKey.constructor, TLRPC.TL_decryptedMessageActionAcceptKey.class);
classStore.put(TLRPC.TL_decryptedMessageActionCommitKey.constructor, TLRPC.TL_decryptedMessageActionCommitKey.class);
classStore.put(TLRPC.TL_decryptedMessageActionAbortKey.constructor, TLRPC.TL_decryptedMessageActionAbortKey.class);
classStore.put(TLRPC.TL_decryptedMessageActionNoop.constructor, TLRPC.TL_decryptedMessageActionNoop.class);
classStore.put(TLRPC.TL_msg_container.constructor, TLRPC.TL_msg_container.class);
classStore.put(TLRPC.TL_fileEncryptedLocation.constructor, TLRPC.TL_fileEncryptedLocation.class);
@ -371,6 +398,7 @@ public class TLClassStore {
classStore.put(TLRPC.TL_userForeign_old.constructor, TLRPC.TL_userForeign_old.class);
classStore.put(TLRPC.TL_userDeleted_old.constructor, TLRPC.TL_userDeleted_old.class);
classStore.put(TLRPC.TL_messageEncryptedAction.constructor, TLRPC.TL_messageEncryptedAction.class);
classStore.put(TLRPC.TL_decryptedMessageHolder.constructor, TLRPC.TL_decryptedMessageHolder.class);
}
static TLClassStore store = null;

View File

@ -272,20 +272,20 @@ public class TLRPC {
}
public static class TL_contactStatus extends TLObject {
public static int constructor = 0xaa77b873;
public static int constructor = 0xd3680c61;
public int user_id;
public int expires;
public UserStatus status;
public void readParams(AbsSerializedData stream) {
user_id = stream.readInt32();
expires = stream.readInt32();
status = (UserStatus)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32());
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeInt32(user_id);
stream.writeInt32(expires);
status.serializeToStream(stream);
}
}
@ -769,6 +769,51 @@ public class TLRPC {
}
}
public static class TL_privacyKeyStatusTimestamp extends TLObject {
public static int constructor = 0xbc2eab30;
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_account_privacyRules extends TLObject {
public static int constructor = 0x554abb6f;
public ArrayList<PrivacyRule> rules = new ArrayList<PrivacyRule>();
public ArrayList<User> users = new ArrayList<User>();
public void readParams(AbsSerializedData stream) {
stream.readInt32();
int count = stream.readInt32();
for (int a = 0; a < count; a++) {
rules.add((PrivacyRule)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()));
}
stream.readInt32();
count = stream.readInt32();
for (int a = 0; a < count; a++) {
users.add((User)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()));
}
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeInt32(0x1cb5c415);
int count = rules.size();
stream.writeInt32(count);
for (int a = 0; a < count; a++) {
rules.get(a).serializeToStream(stream);
}
stream.writeInt32(0x1cb5c415);
count = users.size();
stream.writeInt32(count);
for (int a = 0; a < count; a++) {
users.get(a).serializeToStream(stream);
}
}
}
public static class help_AppUpdate extends TLObject {
public int id;
public boolean critical;
@ -1434,6 +1479,92 @@ public class TLRPC {
}
}
public static class PrivacyRule extends TLObject {
public ArrayList<Integer> users = new ArrayList<Integer>();
}
public static class TL_privacyValueAllowUsers extends PrivacyRule {
public static int constructor = 0x4d5bbe0c;
public void readParams(AbsSerializedData stream) {
stream.readInt32();
int count = stream.readInt32();
for (int a = 0; a < count; a++) {
users.add(stream.readInt32());
}
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeInt32(0x1cb5c415);
int count = users.size();
stream.writeInt32(count);
for (Integer user : users) {
stream.writeInt32(user);
}
}
}
public static class TL_privacyValueDisallowAll extends PrivacyRule {
public static int constructor = 0x8b73e763;
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_privacyValueAllowContacts extends PrivacyRule {
public static int constructor = 0xfffe1bac;
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_privacyValueDisallowContacts extends PrivacyRule {
public static int constructor = 0xf888fa1a;
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_privacyValueAllowAll extends PrivacyRule {
public static int constructor = 0x65427b82;
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_privacyValueDisallowUsers extends PrivacyRule {
public static int constructor = 0xc7f49b7;
public void readParams(AbsSerializedData stream) {
stream.readInt32();
int count = stream.readInt32();
for (int a = 0; a < count; a++) {
users.add(stream.readInt32());
}
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeInt32(0x1cb5c415);
int count = users.size();
stream.writeInt32(count);
for (Integer user : users) {
stream.writeInt32(user);
}
}
}
public static class contacts_Contacts extends TLObject {
public ArrayList<TL_contact> contacts = new ArrayList<TL_contact>();
public ArrayList<User> users = new ArrayList<User>();
@ -1482,6 +1613,15 @@ public class TLRPC {
}
}
public static class TL_inputPrivacyKeyStatusTimestamp extends TLObject {
public static int constructor = 0x4f96cb18;
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class photos_Photos extends TLObject {
public ArrayList<Photo> photos = new ArrayList<Photo>();
public ArrayList<User> users = new ArrayList<User>();
@ -2841,6 +2981,92 @@ public class TLRPC {
}
}
public static class InputPrivacyRule extends TLObject {
public ArrayList<InputUser> users = new ArrayList<InputUser>();
}
public static class TL_inputPrivacyValueDisallowUsers extends InputPrivacyRule {
public static int constructor = 0x90110467;
public void readParams(AbsSerializedData stream) {
stream.readInt32();
int count = stream.readInt32();
for (int a = 0; a < count; a++) {
users.add((InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()));
}
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeInt32(0x1cb5c415);
int count = users.size();
stream.writeInt32(count);
for (InputUser user : users) {
user.serializeToStream(stream);
}
}
}
public static class TL_inputPrivacyValueDisallowAll extends InputPrivacyRule {
public static int constructor = 0xd66b66c9;
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_inputPrivacyValueDisallowContacts extends InputPrivacyRule {
public static int constructor = 0xba52007;
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_inputPrivacyValueAllowAll extends InputPrivacyRule {
public static int constructor = 0x184b35ce;
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_inputPrivacyValueAllowContacts extends InputPrivacyRule {
public static int constructor = 0xd09e07b;
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_inputPrivacyValueAllowUsers extends InputPrivacyRule {
public static int constructor = 0x131cc67f;
public void readParams(AbsSerializedData stream) {
stream.readInt32();
int count = stream.readInt32();
for (int a = 0; a < count; a++) {
users.add((InputUser)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()));
}
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeInt32(0x1cb5c415);
int count = users.size();
stream.writeInt32(count);
for (InputUser user : users) {
user.serializeToStream(stream);
}
}
}
public static class InputMedia extends TLObject {
public String phone_number;
public String first_name;
@ -3449,6 +3675,9 @@ public class TLRPC {
public long random_id;
public ArrayList<TL_dcOption> dc_options = new ArrayList<TL_dcOption>();
public ChatParticipants participants;
public String phone;
public TL_privacyKeyStatusTimestamp key;
public ArrayList<PrivacyRule> rules = new ArrayList<PrivacyRule>();
public EncryptedChat chat;
public boolean blocked;
public long auth_key_id;
@ -3781,6 +4010,47 @@ public class TLRPC {
}
}
public static class TL_updateUserPhone extends Update {
public static int constructor = 0x12b9417b;
public void readParams(AbsSerializedData stream) {
user_id = stream.readInt32();
phone = stream.readString();
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeInt32(user_id);
stream.writeString(phone);
}
}
public static class TL_updatePrivacy extends Update {
public static int constructor = 0xee3b272a;
public void readParams(AbsSerializedData stream) {
key = (TL_privacyKeyStatusTimestamp)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32());
stream.readInt32();
int count = stream.readInt32();
for (int a = 0; a < count; a++) {
rules.add((PrivacyRule)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()));
}
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
key.serializeToStream(stream);
stream.writeInt32(0x1cb5c415);
int count = rules.size();
stream.writeInt32(count);
for (PrivacyRule rule : rules) {
rule.serializeToStream(stream);
}
}
}
public static class TL_updateEncryption extends Update {
public static int constructor = 0xb4a2e88d;
@ -4076,10 +4346,14 @@ public class TLRPC {
public static class DecryptedMessageAction extends TLObject {
public int start_seq_no;
public int end_seq_no;
public int layer;
public int ttl_seconds;
public int layer;
public ArrayList<Long> random_ids = new ArrayList<Long>();
public long exchange_id;
public long key_fingerprint;
public byte[] g_b;
public SendMessageAction action;
public byte[] g_a;
}
public static class TL_decryptedMessageActionSetMessageTTL extends DecryptedMessageAction {
@ -4103,6 +4377,24 @@ public class TLRPC {
}
}
public static class TL_decryptedMessageActionAcceptKey extends DecryptedMessageAction {
public static int constructor = 0x6fe1735b;
public void readParams(AbsSerializedData stream) {
exchange_id = stream.readInt64();
g_b = stream.readByteArray();
key_fingerprint = stream.readInt64();
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeInt64(exchange_id);
stream.writeByteArray(g_b);
stream.writeInt64(key_fingerprint);
}
}
public static class TL_decryptedMessageActionResend extends DecryptedMessageAction {
public static int constructor = 0x511110b0;
@ -4156,6 +4448,22 @@ public class TLRPC {
}
}
public static class TL_decryptedMessageActionRequestKey extends DecryptedMessageAction {
public static int constructor = 0xf3c9611b;
public void readParams(AbsSerializedData stream) {
exchange_id = stream.readInt64();
g_a = stream.readByteArray();
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeInt64(exchange_id);
stream.writeByteArray(g_a);
}
}
public static class TL_decryptedMessageActionTyping extends DecryptedMessageAction {
public static int constructor = 0xccb27641;
@ -4627,6 +4935,107 @@ public class TLRPC {
}
}
public static class TL_account_getPrivacy extends TLObject {
public static int constructor = 0xdadbc950;
public TL_inputPrivacyKeyStatusTimestamp key;
public Class responseClass () {
return TL_account_privacyRules.class;
}
public void readParams(AbsSerializedData stream) {
key = (TL_inputPrivacyKeyStatusTimestamp)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32());
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
key.serializeToStream(stream);
}
}
public static class TL_account_setPrivacy extends TLObject {
public static int constructor = 0xc9f81ce8;
public TL_inputPrivacyKeyStatusTimestamp key;
public ArrayList<InputPrivacyRule> rules = new ArrayList<InputPrivacyRule>();
public Class responseClass () {
return TL_account_privacyRules.class;
}
public void readParams(AbsSerializedData stream) {
key = (TL_inputPrivacyKeyStatusTimestamp)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32());
stream.readInt32();
int count = stream.readInt32();
for (int a = 0; a < count; a++) {
rules.add((InputPrivacyRule)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32()));
}
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
key.serializeToStream(stream);
stream.writeInt32(0x1cb5c415);
int count = rules.size();
stream.writeInt32(count);
for (InputPrivacyRule rule : rules) {
rule.serializeToStream(stream);
}
}
}
public static class TL_account_deleteAccount extends TLObject {
public static int constructor = 0x418d4e0b;
public String reason;
public Class responseClass () {
return Bool.class;
}
public void readParams(AbsSerializedData stream) {
reason = stream.readString();
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeString(reason);
}
}
public static class TL_account_getAccountTTL extends TLObject {
public static int constructor = 0x8fc711d;
public Class responseClass () {
return TL_accountDaysTTL.class;
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_account_setAccountTTL extends TLObject {
public static int constructor = 0x2442485e;
public TL_accountDaysTTL ttl;
public Class responseClass () {
return Bool.class;
}
public void readParams(AbsSerializedData stream) {
ttl = (TL_accountDaysTTL)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32());
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
ttl.serializeToStream(stream);
}
}
public static class TL_account_checkUsername extends TLObject {
public static int constructor = 0x2714d86c;
@ -4665,6 +5074,69 @@ public class TLRPC {
}
}
public static class TL_contacts_resolveUsername extends TLObject {
public static int constructor = 0xbf0131c;
public String username;
public Class responseClass () {
return User.class;
}
public void readParams(AbsSerializedData stream) {
username = stream.readString();
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeString(username);
}
}
public static class TL_account_sendChangePhoneCode extends TLObject {
public static int constructor = 0xa407a8f4;
public String phone_number;
public Class responseClass () {
return TL_account_sentChangePhoneCode.class;
}
public void readParams(AbsSerializedData stream) {
phone_number = stream.readString();
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeString(phone_number);
}
}
public static class TL_account_changePhone extends TLObject {
public static int constructor = 0x70c32edb;
public String phone_number;
public String phone_code_hash;
public String phone_code;
public Class responseClass () {
return User.class;
}
public void readParams(AbsSerializedData stream) {
phone_number = stream.readString();
phone_code_hash = stream.readString();
phone_code = stream.readString();
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeString(phone_number);
stream.writeString(phone_code_hash);
stream.writeString(phone_code);
}
}
public static class InputAudio extends TLObject {
public long id;
public long access_hash;
@ -5546,6 +6018,24 @@ public class TLRPC {
}
}
public static class TL_account_sentChangePhoneCode extends TLObject {
public static int constructor = 0xa4f58c4c;
public String phone_code_hash;
public int send_call_timeout;
public void readParams(AbsSerializedData stream) {
phone_code_hash = stream.readString();
send_call_timeout = stream.readInt32();
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeString(phone_code_hash);
stream.writeInt32(send_call_timeout);
}
}
public static class InputFile extends TLObject {
public long id;
public int parts;
@ -6267,6 +6757,21 @@ public class TLRPC {
}
}
public static class TL_accountDaysTTL extends TLObject {
public static int constructor = 0xb8d0afdf;
public int days;
public void readParams(AbsSerializedData stream) {
days = stream.readInt32();
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeInt32(days);
}
}
public static class InputPeer extends TLObject {
public int user_id;
public int chat_id;
@ -7020,6 +7525,18 @@ public class TLRPC {
public static class TL_contacts_getStatuses extends TLObject {
public static int constructor = 0xc4a353ee;
public ArrayList<TL_contactStatus> id = new ArrayList<TL_contactStatus>();
public Class responseClass () {
return Vector.class;
}
public void parseVector(Vector vector, AbsSerializedData data) {
int size = data.readInt32();
for (int a = 0; a < size; a++) {
vector.objects.add(TLClassStore.Instance().TLdeserialize(data, data.readInt32()));
}
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
@ -8431,6 +8948,38 @@ public class TLRPC {
//manually created
public static class TL_decryptedMessageHolder extends TLObject {
public static int constructor = 0x555555F9;
public long random_id;
public int date;
public TL_decryptedMessageLayer layer;
public EncryptedFile file;
public boolean new_key_used;
public void readParams(AbsSerializedData stream) {
random_id = stream.readInt64();
date = stream.readInt32();
layer = (TL_decryptedMessageLayer)TLClassStore.Instance().TLdeserialize(stream, stream.readInt32());
if (stream.readBool()) {
file = (EncryptedFile) TLClassStore.Instance().TLdeserialize(stream, stream.readInt32());
}
new_key_used = stream.readBool();
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeInt64(random_id);
stream.writeInt32(date);
layer.serializeToStream(stream);
stream.writeBool(file != null);
if (file != null) {
file.serializeToStream(stream);
}
stream.writeBool(new_key_used);
}
}
public static class TL_messages_sendEncryptedService extends TLObject {
public static int constructor = 0x32d439a4;
@ -8715,6 +9264,15 @@ public class TLRPC {
public int expires;
}
public static class TL_userStatusLastWeek extends UserStatus {
public static int constructor = 0x7bf09fc;
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_userStatusEmpty extends UserStatus {
public static int constructor = 0x9d05049;
@ -8724,6 +9282,15 @@ public class TLRPC {
}
}
public static class TL_userStatusLastMonth extends UserStatus {
public static int constructor = 0x77ebc742;
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_userStatusOnline extends UserStatus {
public static int constructor = 0xedb93949;
@ -8738,6 +9305,15 @@ public class TLRPC {
}
}
public static class TL_userStatusRecently extends UserStatus {
public static int constructor = 0xe26f42f1;
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_userStatusOffline extends UserStatus {
public static int constructor = 0x8c703f;
@ -9553,6 +10129,13 @@ public class TLRPC {
public int layer;
public int seq_in;
public int seq_out;
public byte[] key_hash;
public short key_use_count_in;
public short key_use_count_out;
public long exchange_id;
public int key_create_date;
public long future_key_fingerprint;
public byte[] future_auth_key;
}
public static class FileLocation extends TLObject {
@ -9850,13 +10433,15 @@ public class TLRPC {
}
}
public static class invokeWithLayer18 extends TLObject {
public static int constructor = 0x1c900537;
public static class invokeWithLayer extends TLObject {
public static int constructor = 0xda9b0d0d;
public int layer = 20;
public TLObject query;
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeInt32(layer);
query.serializeToStream(stream);
}
}
@ -9967,6 +10552,45 @@ public class TLRPC {
}
}
public static class TL_decryptedMessageActionCommitKey extends DecryptedMessageAction {
public static int constructor = 0xec2e0b9b;
public void readParams(AbsSerializedData stream) {
exchange_id = stream.readInt64();
key_fingerprint = stream.readInt64();
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeInt64(exchange_id);
stream.writeInt64(key_fingerprint);
}
}
public static class TL_decryptedMessageActionAbortKey extends DecryptedMessageAction {
public static int constructor = 0xdd05ec6b;
public void readParams(AbsSerializedData stream) {
exchange_id = stream.readInt64();
}
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
stream.writeInt64(exchange_id);
}
}
public static class TL_decryptedMessageActionNoop extends DecryptedMessageAction {
public static int constructor = 0xa82fdd63;
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_decryptedMessageActionScreenshotMessages extends DecryptedMessageAction {
public static int constructor = 0x8ac1f475;

View File

@ -13,7 +13,6 @@ import android.content.SharedPreferences;
import android.util.Base64;
import org.telegram.android.MessagesStorage;
import org.telegram.ui.ApplicationLoader;
import java.io.File;

View File

@ -14,6 +14,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
@ -27,8 +28,6 @@ import net.hockeyapp.android.CrashManager;
import net.hockeyapp.android.CrashManagerListener;
import net.hockeyapp.android.UpdateManager;
import org.telegram.ui.ApplicationLoader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
@ -107,13 +106,13 @@ public class Utilities {
}
public native static long doPQNative(long _what);
public native static void loadBitmap(String path, int[] bitmap, int scale, int format, int width, int height);
public native static void loadBitmap(String path, Bitmap bitmap, int scale, int width, int height, int stride);
public native static void blurBitmap(Object bitmap, int radius);
public native static int convertVideoFrame(ByteBuffer src, ByteBuffer dest, int destFormat, int width, int height, int padding, int swap);
private native static void aesIgeEncryption(ByteBuffer buffer, byte[] key, byte[] iv, boolean encrypt, int offset, int length);
public static void aesIgeEncryption(ByteBuffer buffer, byte[] key, byte[] iv, boolean encrypt, boolean changeIv, int offset, int length) {
aesIgeEncryption(buffer, key, changeIv ? iv : iv.clone(), encrypt, offset, length);
aesIgeEncryption(buffer, key, changeIv ? iv : (byte [])iv.clone(), encrypt, offset, length);
}
public static Integer parseInt(String value) {
@ -639,7 +638,7 @@ public class Utilities {
builder.append(" ");
}
query.trim();
builder.append(Html.fromHtml("<font color=\"#357aa8\">" + query + "</font>"));
builder.append(Html.fromHtml("<font color=\"#4d83b3\">" + query + "</font>"));
lastIndex = end;
}

View File

@ -6,17 +6,17 @@
* Copyright Nikolai Kudashov, 2013-2014.
*/
package org.telegram.ui.Views.ActionBar;
package org.telegram.ui.ActionBar;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@ -24,7 +24,7 @@ import android.widget.TextView;
import org.telegram.android.AndroidUtilities;
import org.telegram.messenger.R;
public class ActionBarLayer extends FrameLayout {
public class ActionBar extends FrameLayout {
public static class ActionBarMenuOnItemClick {
public void onItemClick(int id) {
@ -36,96 +36,49 @@ public class ActionBarLayer extends FrameLayout {
}
}
private FrameLayout backButtonFrameLayout;
private ImageView logoImageView;
private FrameLayout titleFrameLayout;
private ImageView backButtonImageView;
private TextView titleTextView;
private TextView subTitleTextView;
private View actionModeTop;
private ActionBarMenu menu;
private ActionBarMenu actionMode;
private int logoResourceId;
private int backResourceId;
protected ActionBar parentActionBar;
private boolean oldUseLogo;
private boolean oldUseBack;
private View actionOverlay;
private boolean occupyStatusBar = Build.VERSION.SDK_INT >= 21;
private boolean allowOverlayTitle;
private CharSequence lastTitle;
private boolean showingOverlayTitle;
protected boolean isSearchFieldVisible;
protected int itemsBackgroundResourceId;
private boolean isBackOverlayVisible;
protected BaseFragment parentFragment;
public ActionBarMenuOnItemClick actionBarMenuOnItemClick;
private int leftMargin = 0;
private int extraHeight;
public ActionBarLayer(Context context, ActionBar actionBar) {
public ActionBar(Context context) {
super(context);
parentActionBar = actionBar;
backButtonFrameLayout = new FrameLayout(context);
addView(backButtonFrameLayout);
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)backButtonFrameLayout.getLayoutParams();
titleFrameLayout = new FrameLayout(context);
addView(titleFrameLayout);
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)titleFrameLayout.getLayoutParams();
layoutParams.width = LayoutParams.WRAP_CONTENT;
layoutParams.height = LayoutParams.FILL_PARENT;
layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
backButtonFrameLayout.setLayoutParams(layoutParams);
backButtonFrameLayout.setPadding(0, 0, AndroidUtilities.dp(4), 0);
backButtonFrameLayout.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (isSearchFieldVisible) {
closeSearchField();
return;
}
if (actionBarMenuOnItemClick != null) {
actionBarMenuOnItemClick.onItemClick(-1);
}
}
});
backButtonFrameLayout.setEnabled(false);
}
public ActionBarLayer(Context context) {
super(context);
}
public ActionBarLayer(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ActionBarLayer(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setExtraLeftMargin(int margin) {
leftMargin = margin;
titleFrameLayout.setLayoutParams(layoutParams);
titleFrameLayout.setPadding(0, 0, AndroidUtilities.dp(4), 0);
titleFrameLayout.setEnabled(false);
}
private void positionBackImage(int height) {
if (backButtonImageView != null) {
LayoutParams layoutParams = (LayoutParams)backButtonImageView.getLayoutParams();
layoutParams.width = LayoutParams.WRAP_CONTENT;
layoutParams.height = LayoutParams.WRAP_CONTENT;
layoutParams.width = AndroidUtilities.dp(54);
layoutParams.height = height;
layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
layoutParams.setMargins(AndroidUtilities.dp(3 + leftMargin), (height - backButtonImageView.getDrawable().getIntrinsicHeight()) / 2, 0, 0);
backButtonImageView.setLayoutParams(layoutParams);
}
}
private void positionLogoImage(int height) {
if (logoImageView != null && logoImageView.getDrawable() != null) {
LayoutParams layoutParams = (LayoutParams) logoImageView.getLayoutParams();
if (!AndroidUtilities.isTablet() && getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
layoutParams.width = (int)(logoImageView.getDrawable().getIntrinsicWidth() / 1.3f);
layoutParams.height = (int)(logoImageView.getDrawable().getIntrinsicHeight() / 1.3f);
layoutParams.setMargins(AndroidUtilities.dp(12), (height - layoutParams.height) / 2, 0, 0);
} else {
layoutParams.width = logoImageView.getDrawable().getIntrinsicWidth();
layoutParams.height = logoImageView.getDrawable().getIntrinsicHeight();
layoutParams.setMargins(AndroidUtilities.dp(12), (height - layoutParams.width) / 2, 0, 0);
}
layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
logoImageView.setLayoutParams(layoutParams);
}
}
private void positionTitle(int width, int height) {
int offset = AndroidUtilities.dp(2);
if (!AndroidUtilities.isTablet() && getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
@ -137,9 +90,9 @@ public class ActionBarLayer extends FrameLayout {
if (titleTextView != null && titleTextView.getVisibility() == VISIBLE) {
if (!AndroidUtilities.isTablet() && getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
titleTextView.setTextSize(16);
} else {
titleTextView.setTextSize(18);
} else {
titleTextView.setTextSize(20);
}
layoutParams = (LayoutParams) titleTextView.getLayoutParams();
@ -152,9 +105,9 @@ public class ActionBarLayer extends FrameLayout {
}
if (subTitleTextView != null && subTitleTextView.getVisibility() == VISIBLE) {
if (!AndroidUtilities.isTablet() && getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
subTitleTextView.setTextSize(12);
} else {
subTitleTextView.setTextSize(14);
} else {
subTitleTextView.setTextSize(16);
}
layoutParams = (LayoutParams) subTitleTextView.getLayoutParams();
@ -167,13 +120,17 @@ public class ActionBarLayer extends FrameLayout {
}
int x = 0;
if (logoImageView == null || logoImageView.getVisibility() == GONE) {
x = AndroidUtilities.dp(16 + leftMargin);
if (backButtonImageView != null) {
if (AndroidUtilities.isTablet()) {
x = AndroidUtilities.dp(80);
} else {
if (!AndroidUtilities.isTablet() && getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
x = AndroidUtilities.dp(22 + leftMargin) + (logoImageView.getDrawable() != null ? (int)(logoImageView.getDrawable().getIntrinsicWidth() / 1.3f) : 0);
x = AndroidUtilities.dp(72);
}
} else {
x = AndroidUtilities.dp(22 + leftMargin) + (logoImageView.getDrawable() != null ? logoImageView.getDrawable().getIntrinsicWidth() : 0);
if (AndroidUtilities.isTablet()) {
x = AndroidUtilities.dp(26);
} else {
x = AndroidUtilities.dp(18);
}
}
@ -185,9 +142,11 @@ public class ActionBarLayer extends FrameLayout {
layoutParams = (LayoutParams) titleTextView.getLayoutParams();
layoutParams.width = LayoutParams.MATCH_PARENT;
layoutParams.height = titleTextView.getMeasuredHeight();
int y = (height - titleTextView.getMeasuredHeight()) / 2;
int y;
if (subTitleTextView != null && subTitleTextView.getVisibility() == VISIBLE) {
y = (height / 2 - titleTextView.getMeasuredHeight()) / 2 + offset;
} else {
y = (height - titleTextView.getMeasuredHeight()) / 2 - AndroidUtilities.dp(1);
}
layoutParams.setMargins(x, y, 0, 0);
titleTextView.setLayoutParams(layoutParams);
@ -200,9 +159,10 @@ public class ActionBarLayer extends FrameLayout {
subTitleTextView.setLayoutParams(layoutParams);
}
ViewGroup.LayoutParams layoutParams1 = backButtonFrameLayout.getLayoutParams();
MarginLayoutParams layoutParams1 = (MarginLayoutParams) titleFrameLayout.getLayoutParams();
layoutParams1.width = x + maxTextWidth + (isSearchFieldVisible ? 0 : AndroidUtilities.dp(6));
backButtonFrameLayout.setLayoutParams(layoutParams1);
layoutParams1.topMargin = occupyStatusBar ? AndroidUtilities.statusBarHeight : 0;
titleFrameLayout.setLayoutParams(layoutParams1);
}
public void positionMenu(int width, int height) {
@ -211,43 +171,55 @@ public class ActionBarLayer extends FrameLayout {
}
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)menu.getLayoutParams();
layoutParams.width = isSearchFieldVisible ? LayoutParams.MATCH_PARENT : LayoutParams.WRAP_CONTENT;
layoutParams.leftMargin = isSearchFieldVisible ? AndroidUtilities.dp(26) + logoImageView.getDrawable().getIntrinsicWidth() : 0;
layoutParams.height = height;
layoutParams.leftMargin = isSearchFieldVisible ? AndroidUtilities.dp(54) : 0;
layoutParams.topMargin = occupyStatusBar ? AndroidUtilities.statusBarHeight : 0;
menu.setLayoutParams(layoutParams);
menu.measure(width, height);
}
public void setDisplayUseLogoEnabled(boolean value, int resource) {
if (value && logoImageView == null) {
logoResourceId = resource;
logoImageView = new ImageView(getContext());
logoImageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
backButtonFrameLayout.addView(logoImageView);
}
if (logoImageView != null) {
logoImageView.setVisibility(value ? VISIBLE : GONE);
logoImageView.setImageResource(resource);
positionLogoImage(getMeasuredHeight());
}
}
public void setDisplayHomeAsUpEnabled(boolean value, int resource) {
if (value && backButtonImageView == null) {
backResourceId = resource;
backButtonImageView = new ImageView(getContext());
backButtonFrameLayout.addView(backButtonImageView);
}
private void createBackButtonImage() {
if (backButtonImageView != null) {
backButtonImageView.setVisibility(value ? VISIBLE : GONE);
backButtonFrameLayout.setEnabled(value);
backButtonImageView.setImageResource(resource);
positionBackImage(getMeasuredHeight());
return;
}
backButtonImageView = new ImageView(getContext());
titleFrameLayout.addView(backButtonImageView);
backButtonImageView.setScaleType(ImageView.ScaleType.CENTER);
backButtonImageView.setBackgroundResource(itemsBackgroundResourceId);
backButtonImageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (isSearchFieldVisible) {
closeSearchField();
return;
}
if (actionBarMenuOnItemClick != null) {
actionBarMenuOnItemClick.onItemClick(-1);
}
}
});
}
public void setSubtitle(CharSequence value) {
if (value != null && subTitleTextView == null) {
public void setBackButtonDrawable(Drawable drawable) {
if (backButtonImageView == null) {
createBackButtonImage();
}
backButtonImageView.setImageDrawable(drawable);
}
public void setBackButtonImage(int resource) {
if (backButtonImageView == null) {
createBackButtonImage();
}
backButtonImageView.setImageResource(resource);
}
private void createSubtitleTextView() {
if (subTitleTextView != null) {
return;
}
subTitleTextView = new TextView(getContext());
backButtonFrameLayout.addView(subTitleTextView);
titleFrameLayout.addView(subTitleTextView);
subTitleTextView.setGravity(Gravity.LEFT);
subTitleTextView.setTextColor(0xffd7e8f7);
subTitleTextView.setSingleLine(true);
@ -255,8 +227,13 @@ public class ActionBarLayer extends FrameLayout {
subTitleTextView.setMaxLines(1);
subTitleTextView.setEllipsize(TextUtils.TruncateAt.END);
}
public void setSubtitle(CharSequence value) {
if (value != null && subTitleTextView == null) {
createSubtitleTextView();
}
if (subTitleTextView != null) {
subTitleTextView.setVisibility(value != null ? VISIBLE : GONE);
subTitleTextView.setVisibility(value != null && !isSearchFieldVisible ? VISIBLE : GONE);
subTitleTextView.setText(value);
positionTitle(getMeasuredWidth(), getMeasuredHeight());
}
@ -264,14 +241,7 @@ public class ActionBarLayer extends FrameLayout {
public void setSubTitleIcon(int resourceId, Drawable drawable, int padding) {
if ((resourceId != 0 || drawable != null) && subTitleTextView == null) {
subTitleTextView = new TextView(getContext());
backButtonFrameLayout.addView(subTitleTextView);
subTitleTextView.setGravity(Gravity.LEFT);
subTitleTextView.setTextColor(0xffd7e8f7);
subTitleTextView.setSingleLine(true);
subTitleTextView.setLines(1);
subTitleTextView.setMaxLines(1);
subTitleTextView.setEllipsize(TextUtils.TruncateAt.END);
createSubtitleTextView();
positionTitle(getMeasuredWidth(), getMeasuredHeight());
}
if (subTitleTextView != null) {
@ -284,19 +254,28 @@ public class ActionBarLayer extends FrameLayout {
}
}
public void setTitle(CharSequence value) {
if (value != null && titleTextView == null) {
private void createTitleTextView() {
if (titleTextView != null) {
return;
}
titleTextView = new TextView(getContext());
titleTextView.setGravity(Gravity.LEFT);
titleTextView.setSingleLine(true);
titleTextView.setLines(1);
titleTextView.setMaxLines(1);
titleTextView.setEllipsize(TextUtils.TruncateAt.END);
backButtonFrameLayout.addView(titleTextView);
titleFrameLayout.addView(titleTextView);
titleTextView.setTextColor(0xffffffff);
titleTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
}
public void setTitle(CharSequence value) {
if (value != null && titleTextView == null) {
createTitleTextView();
}
if (titleTextView != null) {
titleTextView.setVisibility(value != null ? VISIBLE : GONE);
lastTitle = value;
titleTextView.setVisibility(value != null && !isSearchFieldVisible ? VISIBLE : GONE);
titleTextView.setText(value);
positionTitle(getMeasuredWidth(), getMeasuredHeight());
}
@ -304,14 +283,7 @@ public class ActionBarLayer extends FrameLayout {
public void setTitleIcon(int resourceId, int padding) {
if (resourceId != 0 && titleTextView == null) {
titleTextView = new TextView(getContext());
titleTextView.setGravity(Gravity.LEFT);
backButtonFrameLayout.addView(titleTextView);
titleTextView.setTextColor(0xffffffff);
titleTextView.setSingleLine(true);
titleTextView.setLines(1);
titleTextView.setMaxLines(1);
titleTextView.setEllipsize(TextUtils.TruncateAt.END);
createTitleTextView();
positionTitle(getMeasuredWidth(), getMeasuredHeight());
}
titleTextView.setCompoundDrawablesWithIntrinsicBounds(resourceId, 0, 0, 0);
@ -333,7 +305,7 @@ public class ActionBarLayer extends FrameLayout {
if (menu != null) {
return menu;
}
menu = new ActionBarMenu(getContext(), parentActionBar, this);
menu = new ActionBarMenu(getContext(), this);
addView(menu);
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)menu.getLayoutParams();
layoutParams.height = LayoutParams.FILL_PARENT;
@ -343,10 +315,6 @@ public class ActionBarLayer extends FrameLayout {
return menu;
}
public void onDestroy() {
parentActionBar.detachActionBarLayer(this);
}
public void setActionBarMenuOnItemClick(ActionBarMenuOnItemClick listener) {
actionBarMenuOnItemClick = listener;
}
@ -355,21 +323,40 @@ public class ActionBarLayer extends FrameLayout {
LayoutInflater li = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = li.inflate(resourceId, null);
addView(view);
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)view.getLayoutParams();
layoutParams.width = LayoutParams.FILL_PARENT;
layoutParams.height = LayoutParams.FILL_PARENT;
layoutParams.topMargin = occupyStatusBar ? AndroidUtilities.statusBarHeight : 0;
view.setLayoutParams(layoutParams);
}
public ActionBarMenu createActionMode() {
if (actionMode != null) {
return actionMode;
}
actionMode = new ActionBarMenu(getContext(), parentActionBar, this);
actionMode = new ActionBarMenu(getContext(), this);
actionMode.setBackgroundResource(R.drawable.editheader);
addView(actionMode);
actionMode.setPadding(0, occupyStatusBar ? AndroidUtilities.statusBarHeight : 0, 0, 0);
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)actionMode.getLayoutParams();
layoutParams.height = LayoutParams.FILL_PARENT;
layoutParams.width = LayoutParams.FILL_PARENT;
layoutParams.gravity = Gravity.RIGHT;
actionMode.setLayoutParams(layoutParams);
actionMode.setVisibility(GONE);
if (occupyStatusBar) {
actionModeTop = new View(getContext());
actionModeTop.setBackgroundColor(0x99000000);
addView(actionModeTop);
layoutParams = (FrameLayout.LayoutParams)actionModeTop.getLayoutParams();
layoutParams.height = AndroidUtilities.statusBarHeight;
layoutParams.width = LayoutParams.FILL_PARENT;
layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
actionModeTop.setLayoutParams(layoutParams);
actionModeTop.setVisibility(GONE);
}
return actionMode;
}
@ -378,8 +365,11 @@ public class ActionBarLayer extends FrameLayout {
return;
}
actionMode.setVisibility(VISIBLE);
if (backButtonFrameLayout != null) {
backButtonFrameLayout.setVisibility(INVISIBLE);
if (actionModeTop != null) {
actionModeTop.setVisibility(VISIBLE);
}
if (titleFrameLayout != null) {
titleFrameLayout.setVisibility(INVISIBLE);
}
if (menu != null) {
menu.setVisibility(INVISIBLE);
@ -391,8 +381,11 @@ public class ActionBarLayer extends FrameLayout {
return;
}
actionMode.setVisibility(GONE);
if (backButtonFrameLayout != null) {
backButtonFrameLayout.setVisibility(isSearchFieldVisible || actionOverlay == null || actionOverlay.getVisibility() == GONE ? VISIBLE : INVISIBLE);
if (actionModeTop != null) {
actionModeTop.setVisibility(GONE);
}
if (titleFrameLayout != null) {
titleFrameLayout.setVisibility(VISIBLE);
}
if (menu != null) {
menu.setVisibility(VISIBLE);
@ -411,20 +404,10 @@ public class ActionBarLayer extends FrameLayout {
if (subTitleTextView != null) {
subTitleTextView.setVisibility(visible ? GONE : VISIBLE);
}
backButtonFrameLayout.setPadding(0, 0, visible ? 0 : AndroidUtilities.dp(4), 0);
if (visible) {
oldUseLogo = logoImageView != null && logoImageView.getVisibility() == VISIBLE;
setDisplayUseLogoEnabled(true, R.drawable.ic_ab_search);
} else {
setDisplayUseLogoEnabled(oldUseLogo, logoResourceId);
Drawable drawable = backButtonImageView.getDrawable();
if (drawable != null && drawable instanceof MenuDrawable) {
((MenuDrawable)drawable).setRotation(visible ? 1 : 0, true);
}
if (visible) {
oldUseBack = backButtonImageView != null && backButtonImageView.getVisibility() == VISIBLE;
setDisplayHomeAsUpEnabled(true, R.drawable.ic_ab_back);
} else {
setDisplayHomeAsUpEnabled(oldUseBack, backResourceId);
}
positionBackOverlay(getMeasuredWidth(), getMeasuredHeight());
}
public void closeSearchField() {
@ -436,22 +419,12 @@ public class ActionBarLayer extends FrameLayout {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
positionLogoImage(MeasureSpec.getSize(heightMeasureSpec));
positionBackImage(MeasureSpec.getSize(heightMeasureSpec));
positionMenu(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
positionTitle(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
positionBackOverlay(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
public void setAlpha(float alpha) {
if (menu != null) {
menu.setAlpha(alpha);
}
if (backButtonFrameLayout != null) {
backButtonFrameLayout.setAlpha(alpha);
}
int actionBarHeight = AndroidUtilities.getCurrentActionBarHeight();
positionBackImage(actionBarHeight);
positionMenu(MeasureSpec.getSize(widthMeasureSpec), actionBarHeight);
positionTitle(MeasureSpec.getSize(widthMeasureSpec), actionBarHeight);
actionBarHeight += occupyStatusBar ? AndroidUtilities.statusBarHeight : 0;
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(actionBarHeight + extraHeight, MeasureSpec.EXACTLY));
}
public void onMenuButtonPressed() {
@ -466,51 +439,55 @@ public class ActionBarLayer extends FrameLayout {
}
}
public void setBackOverlay(int resourceId) {
LayoutInflater li = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
actionOverlay = li.inflate(resourceId, null);
addView(actionOverlay);
actionOverlay.setVisibility(GONE);
actionOverlay.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (actionBarMenuOnItemClick != null) {
actionBarMenuOnItemClick.onItemClick(-1);
}
}
});
public void setAllowOverlayTitle(boolean value) {
allowOverlayTitle = value;
}
public void setBackOverlayVisible(boolean visible) {
if (actionOverlay == null || parentFragment == null || parentFragment.parentLayout == null) {
public void setTitleOverlayText(String text) {
if (showingOverlayTitle == (text != null) || !allowOverlayTitle || parentFragment.parentLayout == null) {
return;
}
isBackOverlayVisible = visible;
if (visible) {
parentFragment.parentLayout.onOverlayShow(actionOverlay, parentFragment);
showingOverlayTitle = text != null;
CharSequence textToSet = text != null ? text : lastTitle;
if (textToSet != null && titleTextView == null) {
createTitleTextView();
}
if (titleTextView != null) {
titleTextView.setVisibility(textToSet != null && !isSearchFieldVisible ? VISIBLE : GONE);
titleTextView.setText(textToSet);
positionTitle(getMeasuredWidth(), getMeasuredHeight());
}
positionBackOverlay(getMeasuredWidth(), getMeasuredHeight());
}
private void positionBackOverlay(int widthMeasureSpec, int heightMeasureSpec) {
if (actionOverlay == null) {
return;
public void setExtraHeight(int value, boolean layout) {
extraHeight = value;
if (layout) {
requestLayout();
}
backButtonFrameLayout.setVisibility(isSearchFieldVisible || actionOverlay == null || actionOverlay.getVisibility() == GONE ? VISIBLE : INVISIBLE);
actionOverlay.setVisibility(!isSearchFieldVisible && isBackOverlayVisible ? VISIBLE : GONE);
if (actionOverlay.getVisibility() == VISIBLE) {
ViewGroup.LayoutParams layoutParams = actionOverlay.getLayoutParams();
layoutParams.width = LayoutParams.WRAP_CONTENT;
layoutParams.height = LayoutParams.MATCH_PARENT;
actionOverlay.setLayoutParams(layoutParams);
actionOverlay.measure(widthMeasureSpec, heightMeasureSpec);
layoutParams.width = Math.min(actionOverlay.getMeasuredWidth() + AndroidUtilities.dp(4), widthMeasureSpec - (menu != null ? menu.getMeasuredWidth() : 0));
actionOverlay.setLayoutParams(layoutParams);
}
public int getExtraHeight() {
return extraHeight;
}
public void setOccupyStatusBar(boolean value) {
occupyStatusBar = value;
}
public boolean getOccupyStatusBar() {
return occupyStatusBar;
}
public void setItemsBackground(int resourceId) {
itemsBackgroundResourceId = resourceId;
backButtonFrameLayout.setBackgroundResource(itemsBackgroundResourceId);
if (backButtonImageView != null) {
backButtonImageView.setBackgroundResource(itemsBackgroundResourceId);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
return true;
}
}

View File

@ -0,0 +1,963 @@
/*
* This is the source code of Telegram for Android v. 1.4.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-2014.
*/
package org.telegram.ui.ActionBar;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
import android.view.ActionMode;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import org.telegram.android.AndroidUtilities;
import org.telegram.android.NotificationCenter;
import org.telegram.messenger.R;
import org.telegram.ui.AnimationCompat.AnimatorListenerAdapterProxy;
import org.telegram.ui.AnimationCompat.AnimatorSetProxy;
import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy;
import org.telegram.ui.AnimationCompat.ViewProxy;
import java.util.ArrayList;
public class ActionBarLayout extends FrameLayout {
public static interface ActionBarLayoutDelegate {
public abstract boolean onPreIme();
public abstract boolean needPresentFragment(BaseFragment fragment, boolean removeLast, boolean forceWithoutAnimation, ActionBarLayout layout);
public abstract boolean needAddFragmentToStack(BaseFragment fragment, ActionBarLayout layout);
public abstract boolean needCloseLastFragment(ActionBarLayout layout);
public abstract void onRebuildAllFragments(ActionBarLayout layout);
}
public class LinearLayoutContainer extends LinearLayout {
public LinearLayoutContainer(Context context) {
super(context);
setOrientation(VERTICAL);
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if (child instanceof ActionBar) {
return super.drawChild(canvas, child, drawingTime);
} else {
boolean wasActionBar = false;
int actionBarHeight = 0;
int childCount = getChildCount();
for (int a = 0; a < childCount; a++) {
View view = getChildAt(a);
if (view == child) {
continue;
}
if (view instanceof ActionBar && view.getVisibility() == VISIBLE) {
actionBarHeight = view.getMeasuredHeight();
wasActionBar = true;
break;
}
}
/*if (!wasActionBar) {
if (child instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) child;
childCount = viewGroup.getChildCount();
for (int a = 0; a < childCount; a++) {
View possibleActionBar = viewGroup.getChildAt(a);
if (possibleActionBar instanceof ActionBar) {
actionBarHeight = possibleActionBar.getMeasuredHeight();
break;
}
}
}
}*/
boolean result = super.drawChild(canvas, child, drawingTime);
if (actionBarHeight != 0 && headerShadowDrawable != null) {
headerShadowDrawable.setBounds(0, actionBarHeight, getMeasuredWidth(), actionBarHeight + headerShadowDrawable.getIntrinsicHeight());
headerShadowDrawable.draw(canvas);
}
return result;
}
}
}
private static Drawable headerShadowDrawable;
private static Drawable layerShadowDrawable;
private static Paint scrimPaint;
private LinearLayoutContainer containerView;
private LinearLayoutContainer containerViewBack;
private DrawerLayoutContainer drawerLayoutContainer;
private ActionBar currentActionBar;
private AnimatorSetProxy currentAnimation;
public float innerTranslationX;
private boolean maybeStartTracking;
protected boolean startedTracking;
private int startedTrackingX;
private int startedTrackingY;
protected boolean animationInProgress;
private VelocityTracker velocityTracker;
private boolean beginTrackingSent;
private boolean transitionAnimationInProgress;
private long transitionAnimationStartTime;
private boolean inActionMode;
private int startedTrackingPointerId;
private Runnable onCloseAnimationEndRunnable;
private Runnable onOpenAnimationEndRunnable;
private boolean useAlphaAnimations;
private View backgroundView;
private boolean removeActionBarExtraHeight;
private String titleOverlayText;
private ActionBarLayoutDelegate delegate = null;
protected Activity parentActivity = null;
public ArrayList<BaseFragment> fragmentsStack = null;
public ActionBarLayout(Context context) {
super(context);
parentActivity = (Activity) context;
if (layerShadowDrawable == null) {
layerShadowDrawable = getResources().getDrawable(R.drawable.layer_shadow);
headerShadowDrawable = getResources().getDrawable(R.drawable.header_shadow);
scrimPaint = new Paint();
}
}
public void init(ArrayList<BaseFragment> stack) {
fragmentsStack = stack;
containerViewBack = new LinearLayoutContainer(parentActivity);
addView(containerViewBack);
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) containerViewBack.getLayoutParams();
layoutParams.width = LayoutParams.MATCH_PARENT;
layoutParams.height = LayoutParams.MATCH_PARENT;
layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
containerViewBack.setLayoutParams(layoutParams);
containerView = new LinearLayoutContainer(parentActivity);
addView(containerView);
layoutParams = (FrameLayout.LayoutParams) containerView.getLayoutParams();
layoutParams.width = LayoutParams.MATCH_PARENT;
layoutParams.height = LayoutParams.MATCH_PARENT;
layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
containerView.setLayoutParams(layoutParams);
for (BaseFragment fragment : fragmentsStack) {
fragment.setParentLayout(this);
}
}
@Override
public void onConfigurationChanged(android.content.res.Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (!fragmentsStack.isEmpty()) {
BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 1);
lastFragment.onConfigurationChanged(newConfig);
}
}
public void setInnerTranslationX(float value) {
innerTranslationX = value;
invalidate();
}
public float getInnerTranslationX() {
return innerTranslationX;
}
public void onResume() {
if (transitionAnimationInProgress) {
if (currentAnimation != null) {
currentAnimation.cancel();
currentAnimation = null;
}
if (onCloseAnimationEndRunnable != null) {
onCloseAnimationEnd(false);
} else if (onOpenAnimationEndRunnable != null) {
onOpenAnimationEnd(false);
}
}
if (!fragmentsStack.isEmpty()) {
BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 1);
lastFragment.onResume();
}
}
public void onPause() {
if (!fragmentsStack.isEmpty()) {
BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 1);
lastFragment.onPause();
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return !(!animationInProgress && !checkTransitionAnimation()) || onTouchEvent(ev);
}
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
onTouchEvent(null);
super.requestDisallowInterceptTouchEvent(disallowIntercept);
}
@Override
public boolean dispatchKeyEventPreIme(KeyEvent event) {
if (event != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
return delegate != null && delegate.onPreIme() || super.dispatchKeyEventPreIme(event);
}
return super.dispatchKeyEventPreIme(event);
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
int width = getWidth() - getPaddingLeft() - getPaddingRight();
int translationX = (int) innerTranslationX + getPaddingRight();
int clipLeft = getPaddingLeft();
int clipRight = width + getPaddingLeft();
if (child == containerViewBack) {
clipRight = translationX;
} else if (child == containerView) {
clipLeft = translationX;
}
final int restoreCount = canvas.save();
if (!transitionAnimationInProgress) {
canvas.clipRect(clipLeft, 0, clipRight, getHeight());
}
final boolean result = super.drawChild(canvas, child, drawingTime);
canvas.restoreToCount(restoreCount);
if (translationX != 0) {
if (child == containerView) {
final float alpha = Math.max(0, Math.min((width - translationX) / (float) AndroidUtilities.dp(20), 1.0f));
layerShadowDrawable.setBounds(translationX - layerShadowDrawable.getIntrinsicWidth(), child.getTop(), translationX, child.getBottom());
layerShadowDrawable.setAlpha((int) (0xff * alpha));
layerShadowDrawable.draw(canvas);
} else if (child == containerViewBack) {
final float opacity = Math.min(0.8f, (width - translationX) / (float)width);
scrimPaint.setColor((int) (((0x99000000 & 0xff000000) >>> 24) * opacity) << 24);
canvas.drawRect(clipLeft, 0, clipRight, getHeight(), scrimPaint);
}
}
return result;
}
public void setDelegate(ActionBarLayoutDelegate delegate) {
this.delegate = delegate;
}
private void onSlideAnimationEnd(final boolean backAnimation) {
if (!backAnimation) {
BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 1);
lastFragment.onPause();
lastFragment.onFragmentDestroy();
lastFragment.setParentLayout(null);
fragmentsStack.remove(fragmentsStack.size() - 1);
LinearLayoutContainer temp = containerView;
containerView = containerViewBack;
containerViewBack = temp;
bringChildToFront(containerView);
lastFragment = fragmentsStack.get(fragmentsStack.size() - 1);
currentActionBar = lastFragment.actionBar;
lastFragment.onResume();
} else {
BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 2);
lastFragment.onPause();
if (lastFragment.fragmentView != null) {
ViewGroup parent = (ViewGroup) lastFragment.fragmentView.getParent();
if (parent != null) {
parent.removeView(lastFragment.fragmentView);
}
}
if (lastFragment.needAddActionBar() && lastFragment.actionBar != null) {
ViewGroup parent = (ViewGroup) lastFragment.actionBar.getParent();
if (parent != null) {
parent.removeView(lastFragment.actionBar);
}
}
}
containerViewBack.setVisibility(View.GONE);
//AndroidUtilities.unlockOrientation(parentActivity);
startedTracking = false;
animationInProgress = false;
ViewProxy.setTranslationX(containerView, 0);
ViewProxy.setTranslationX(containerViewBack, 0);
setInnerTranslationX(0);
}
private void prepareForMoving(MotionEvent ev) {
maybeStartTracking = false;
startedTracking = true;
startedTrackingX = (int) ev.getX();
containerViewBack.setVisibility(View.VISIBLE);
beginTrackingSent = false;
BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 2);
View fragmentView = lastFragment.createView(parentActivity.getLayoutInflater(), null);
ViewGroup parent = (ViewGroup) fragmentView.getParent();
if (parent != null) {
parent.removeView(fragmentView);
}
if (lastFragment.needAddActionBar() && lastFragment.actionBar != null) {
parent = (ViewGroup) lastFragment.actionBar.getParent();
if (parent != null) {
parent.removeView(lastFragment.actionBar);
}
if (removeActionBarExtraHeight) {
lastFragment.actionBar.setOccupyStatusBar(false);
}
containerViewBack.addView(lastFragment.actionBar);
lastFragment.actionBar.setTitleOverlayText(titleOverlayText);
}
containerViewBack.addView(fragmentView);
ViewGroup.LayoutParams layoutParams = fragmentView.getLayoutParams();
layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT;
layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT;
fragmentView.setLayoutParams(layoutParams);
if (fragmentView.getBackground() == null) {
fragmentView.setBackgroundColor(0xffffffff);
}
lastFragment.onResume();
//AndroidUtilities.lockOrientation(parentActivity);
}
public boolean onTouchEvent(MotionEvent ev) {
if (!checkTransitionAnimation() && !inActionMode && !animationInProgress) {
if (fragmentsStack.size() > 1) {
if (ev != null && ev.getAction() == MotionEvent.ACTION_DOWN && !startedTracking && !maybeStartTracking) {
BaseFragment currentFragment = fragmentsStack.get(fragmentsStack.size() - 1);
if (!currentFragment.swipeBackEnabled) {
return false;
}
startedTrackingPointerId = ev.getPointerId(0);
maybeStartTracking = true;
startedTrackingX = (int) ev.getX();
startedTrackingY = (int) ev.getY();
if (velocityTracker != null) {
velocityTracker.clear();
}
} else if (ev != null && ev.getAction() == MotionEvent.ACTION_MOVE && ev.getPointerId(0) == startedTrackingPointerId) {
if (velocityTracker == null) {
velocityTracker = VelocityTracker.obtain();
}
int dx = Math.max(0, (int) (ev.getX() - startedTrackingX));
int dy = Math.abs((int) ev.getY() - startedTrackingY);
velocityTracker.addMovement(ev);
if (maybeStartTracking && !startedTracking && dx >= AndroidUtilities.dp(10) && Math.abs(dx) / 3 > dy) {
prepareForMoving(ev);
} else if (startedTracking) {
if (!beginTrackingSent) {
if (parentActivity.getCurrentFocus() != null) {
AndroidUtilities.hideKeyboard(parentActivity.getCurrentFocus());
}
BaseFragment currentFragment = fragmentsStack.get(fragmentsStack.size() - 1);
currentFragment.onBeginSlide();
beginTrackingSent = true;
}
ViewProxy.setTranslationX(containerView, dx);
setInnerTranslationX(dx);
}
} else if (ev != null && ev.getPointerId(0) == startedTrackingPointerId && (ev.getAction() == MotionEvent.ACTION_CANCEL || ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_POINTER_UP)) {
if (velocityTracker == null) {
velocityTracker = VelocityTracker.obtain();
}
velocityTracker.computeCurrentVelocity(1000);
if (!startedTracking) {
float velX = velocityTracker.getXVelocity();
float velY = velocityTracker.getYVelocity();
if (velX >= 3500 && velX > velY) {
prepareForMoving(ev);
if (!beginTrackingSent) {
if (((Activity) getContext()).getCurrentFocus() != null) {
AndroidUtilities.hideKeyboard(((Activity) getContext()).getCurrentFocus());
}
beginTrackingSent = true;
}
}
}
if (startedTracking) {
float x = ViewProxy.getX(containerView);
AnimatorSetProxy animatorSet = new AnimatorSetProxy();
float velX = velocityTracker.getXVelocity();
float velY = velocityTracker.getYVelocity();
final boolean backAnimation = x < containerView.getMeasuredWidth() / 3.0f && (velX < 3500 || velX < velY);
float distToMove = 0;
if (!backAnimation) {
distToMove = containerView.getMeasuredWidth() - x;
animatorSet.playTogether(
ObjectAnimatorProxy.ofFloat(containerView, "x", containerView.getMeasuredWidth()),
ObjectAnimatorProxy.ofFloat(this, "innerTranslationX", (float)containerView.getMeasuredWidth())
);
} else {
distToMove = x;
animatorSet.playTogether(
ObjectAnimatorProxy.ofFloat(containerView, "x", 0),
ObjectAnimatorProxy.ofFloat(this, "innerTranslationX", 0.0f)
);
}
animatorSet.setDuration(Math.max((int) (200.0f / containerView.getMeasuredWidth() * distToMove), 50));
animatorSet.addListener(new AnimatorListenerAdapterProxy() {
@Override
public void onAnimationEnd(Object animator) {
onSlideAnimationEnd(backAnimation);
}
@Override
public void onAnimationCancel(Object animator) {
onSlideAnimationEnd(backAnimation);
}
});
animatorSet.start();
animationInProgress = true;
} else {
maybeStartTracking = false;
startedTracking = false;
}
if (velocityTracker != null) {
velocityTracker.recycle();
velocityTracker = null;
}
} else if (ev == null) {
maybeStartTracking = false;
startedTracking = false;
if (velocityTracker != null) {
velocityTracker.recycle();
velocityTracker = null;
}
}
}
return startedTracking;
}
return false;
}
public void onBackPressed() {
if (startedTracking || checkTransitionAnimation() || fragmentsStack.isEmpty()) {
return;
}
if (currentActionBar != null && currentActionBar.isSearchFieldVisible) {
currentActionBar.closeSearchField();
return;
}
BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 1);
if (lastFragment.onBackPressed()) {
if (!fragmentsStack.isEmpty()) {
closeLastFragment(true);
}
}
}
public void onLowMemory() {
for (BaseFragment fragment : fragmentsStack) {
fragment.onLowMemory();
}
}
private void onAnimationEndCheck(boolean byCheck) {
onCloseAnimationEnd(false);
onOpenAnimationEnd(false);
if (currentAnimation != null) {
if (byCheck) {
currentAnimation.cancel();
}
currentAnimation = null;
}
ViewProxy.setAlpha(this, 1.0f);
ViewProxy.setAlpha(containerView, 1.0f);
ViewProxy.setScaleX(containerView, 1.0f);
ViewProxy.setScaleY(containerView, 1.0f);
ViewProxy.setAlpha(containerViewBack, 1.0f);
ViewProxy.setScaleX(containerViewBack, 1.0f);
ViewProxy.setScaleY(containerViewBack, 1.0f);
}
public boolean checkTransitionAnimation() {
if (transitionAnimationInProgress && transitionAnimationStartTime < System.currentTimeMillis() - 400) {
transitionAnimationInProgress = false;
onAnimationEndCheck(true);
}
return transitionAnimationInProgress;
}
private void presentFragmentInternalRemoveOld(boolean removeLast, final BaseFragment fragment) {
if (fragment == null) {
return;
}
fragment.onPause();
if (removeLast) {
fragment.onFragmentDestroy();
fragment.setParentLayout(null);
fragmentsStack.remove(fragment);
} else {
if (fragment.fragmentView != null) {
ViewGroup parent = (ViewGroup) fragment.fragmentView.getParent();
if (parent != null) {
parent.removeView(fragment.fragmentView);
}
}
if (fragment.needAddActionBar() && fragment.actionBar != null) {
ViewGroup parent = (ViewGroup) fragment.actionBar.getParent();
if (parent != null) {
parent.removeView(fragment.actionBar);
}
}
}
containerViewBack.setVisibility(View.GONE);
}
public boolean presentFragment(BaseFragment fragment) {
return presentFragment(fragment, false, false, true);
}
public boolean presentFragment(BaseFragment fragment, boolean removeLast) {
return presentFragment(fragment, removeLast, false, true);
}
public boolean presentFragment(final BaseFragment fragment, final boolean removeLast, boolean forceWithoutAnimation, boolean check) {
if (checkTransitionAnimation() || delegate != null && check && !delegate.needPresentFragment(fragment, removeLast, forceWithoutAnimation, this) || !fragment.onFragmentCreate()) {
return false;
}
if (parentActivity.getCurrentFocus() != null) {
AndroidUtilities.hideKeyboard(parentActivity.getCurrentFocus());
NotificationCenter.getInstance().postNotificationName(NotificationCenter.hideEmojiKeyboard);
}
boolean needAnimation = Build.VERSION.SDK_INT > 10 && !forceWithoutAnimation && parentActivity.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE).getBoolean("view_animations", true);
final BaseFragment currentFragment = !fragmentsStack.isEmpty() ? fragmentsStack.get(fragmentsStack.size() - 1) : null;
fragment.setParentLayout(this);
View fragmentView = fragment.createView(parentActivity.getLayoutInflater(), null);
if (fragment.needAddActionBar() && fragment.actionBar != null) {
if (removeActionBarExtraHeight) {
fragment.actionBar.setOccupyStatusBar(false);
}
ViewGroup parent = (ViewGroup) fragment.actionBar.getParent();
if (parent != null) {
parent.removeView(fragment.actionBar);
}
containerViewBack.addView(fragment.actionBar);
fragment.actionBar.setTitleOverlayText(titleOverlayText);
}
containerViewBack.addView(fragmentView);
ViewGroup.LayoutParams layoutParams = fragmentView.getLayoutParams();
layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT;
layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT;
fragmentView.setLayoutParams(layoutParams);
fragmentsStack.add(fragment);
fragment.onResume();
currentActionBar = fragment.actionBar;
if (fragmentView.getBackground() == null) {
fragmentView.setBackgroundColor(0xffffffff);
}
LinearLayoutContainer temp = containerView;
containerView = containerViewBack;
containerViewBack = temp;
containerView.setVisibility(View.VISIBLE);
bringChildToFront(containerView);
if (!needAnimation) {
presentFragmentInternalRemoveOld(removeLast, currentFragment);
if (backgroundView != null) {
backgroundView.setVisibility(VISIBLE);
}
}
if (needAnimation) {
if (useAlphaAnimations && fragmentsStack.size() == 1) {
presentFragmentInternalRemoveOld(removeLast, currentFragment);
ArrayList<Object> animators = new ArrayList<Object>();
animators.add(ObjectAnimatorProxy.ofFloat(this, "alpha", 0.0f, 1.0f));
if (backgroundView != null) {
backgroundView.setVisibility(VISIBLE);
animators.add(ObjectAnimatorProxy.ofFloat(backgroundView, "alpha", 0.0f, 1.0f));
}
currentAnimation = new AnimatorSetProxy();
currentAnimation.playTogether(animators);
currentAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
currentAnimation.setDuration(200);
currentAnimation.addListener(new AnimatorListenerAdapterProxy() {
@Override
public void onAnimationEnd(Object animation) {
onAnimationEndCheck(false);
}
});
currentAnimation.start();
} else {
transitionAnimationStartTime = System.currentTimeMillis();
transitionAnimationInProgress = true;
onOpenAnimationEndRunnable = new Runnable() {
@Override
public void run() {
presentFragmentInternalRemoveOld(removeLast, currentFragment);
fragment.onOpenAnimationEnd();
ViewProxy.setTranslationX(containerView, 0);
}
};
currentAnimation = new AnimatorSetProxy();
currentAnimation.playTogether(
ObjectAnimatorProxy.ofFloat(containerView, "alpha", 0.0f, 1.0f),
ObjectAnimatorProxy.ofFloat(containerView, "translationX", AndroidUtilities.dp(48), 0));
currentAnimation.setInterpolator(new DecelerateInterpolator(1.5f));
currentAnimation.setDuration(200);
currentAnimation.addListener(new AnimatorListenerAdapterProxy() {
@Override
public void onAnimationEnd(Object animation) {
onAnimationEndCheck(false);
}
});
currentAnimation.start();
}
} else {
if (backgroundView != null) {
ViewProxy.setAlpha(backgroundView, 1.0f);
backgroundView.setVisibility(VISIBLE);
}
fragment.onOpenAnimationEnd();
}
return true;
}
public boolean addFragmentToStack(BaseFragment fragment) {
return addFragmentToStack(fragment, -1);
}
public boolean addFragmentToStack(BaseFragment fragment, int position) {
if (delegate != null && !delegate.needAddFragmentToStack(fragment, this) || !fragment.onFragmentCreate()) {
return false;
}
fragment.setParentLayout(this);
if (position == -1) {
fragmentsStack.add(fragment);
} else {
fragmentsStack.add(position, fragment);
}
return true;
}
private void closeLastFragmentInternalRemoveOld(BaseFragment fragment) {
fragment.onPause();
fragment.onFragmentDestroy();
fragment.setParentLayout(null);
fragmentsStack.remove(fragment);
containerViewBack.setVisibility(View.GONE);
bringChildToFront(containerView);
}
public void closeLastFragment(boolean animated) {
if (delegate != null && !delegate.needCloseLastFragment(this) || checkTransitionAnimation()) {
return;
}
if (parentActivity.getCurrentFocus() != null) {
AndroidUtilities.hideKeyboard(parentActivity.getCurrentFocus());
}
boolean needAnimation = Build.VERSION.SDK_INT > 10 && animated && parentActivity.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE).getBoolean("view_animations", true);
final BaseFragment currentFragment = fragmentsStack.get(fragmentsStack.size() - 1);
BaseFragment previousFragment = null;
if (fragmentsStack.size() > 1) {
previousFragment = fragmentsStack.get(fragmentsStack.size() - 2);
}
if (previousFragment != null) {
LinearLayoutContainer temp = containerView;
containerView = containerViewBack;
containerViewBack = temp;
containerView.setVisibility(View.VISIBLE);
previousFragment.setParentLayout(this);
View fragmentView = previousFragment.createView(parentActivity.getLayoutInflater(), null);
if (previousFragment.needAddActionBar() && previousFragment.actionBar != null) {
if (removeActionBarExtraHeight) {
previousFragment.actionBar.setOccupyStatusBar(false);
}
ViewGroup parent = (ViewGroup) previousFragment.actionBar.getParent();
if (parent != null) {
parent.removeView(previousFragment.actionBar);
}
containerView.addView(previousFragment.actionBar);
previousFragment.actionBar.setTitleOverlayText(titleOverlayText);
}
containerView.addView(fragmentView);
ViewGroup.LayoutParams layoutParams = fragmentView.getLayoutParams();
layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT;
layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT;
fragmentView.setLayoutParams(layoutParams);
previousFragment.onResume();
currentActionBar = previousFragment.actionBar;
if (fragmentView.getBackground() == null) {
fragmentView.setBackgroundColor(0xffffffff);
}
if (!needAnimation) {
closeLastFragmentInternalRemoveOld(currentFragment);
}
if (needAnimation) {
transitionAnimationStartTime = System.currentTimeMillis();
transitionAnimationInProgress = true;
onCloseAnimationEndRunnable = new Runnable() {
@Override
public void run() {
closeLastFragmentInternalRemoveOld(currentFragment);
ViewProxy.setTranslationX(containerViewBack, 0);
}
};
currentAnimation = new AnimatorSetProxy();
currentAnimation.playTogether(
ObjectAnimatorProxy.ofFloat(containerViewBack, "alpha", 1.0f, 0.0f),
ObjectAnimatorProxy.ofFloat(containerViewBack, "translationX", 0, AndroidUtilities.dp(48)));
currentAnimation.setInterpolator(new DecelerateInterpolator(1.5f));
currentAnimation.setDuration(200);
currentAnimation.addListener(new AnimatorListenerAdapterProxy() {
@Override
public void onAnimationEnd(Object animation) {
onAnimationEndCheck(false);
}
});
currentAnimation.start();
}
} else {
if (useAlphaAnimations) {
transitionAnimationStartTime = System.currentTimeMillis();
transitionAnimationInProgress = true;
onCloseAnimationEndRunnable = new Runnable() {
@Override
public void run() {
removeFragmentFromStack(currentFragment);
setVisibility(GONE);
if (backgroundView != null) {
backgroundView.setVisibility(GONE);
}
if (drawerLayoutContainer != null) {
drawerLayoutContainer.setAllowOpenDrawer(true);
}
}
};
ArrayList<Object> animators = new ArrayList<Object>();
animators.add(ObjectAnimatorProxy.ofFloat(this, "alpha", 1.0f, 0.0f));
if (backgroundView != null) {
animators.add(ObjectAnimatorProxy.ofFloat(backgroundView, "alpha", 1.0f, 0.0f));
}
currentAnimation = new AnimatorSetProxy();
currentAnimation.playTogether(animators);
currentAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
currentAnimation.setDuration(200);
currentAnimation.addListener(new AnimatorListenerAdapterProxy() {
@Override
public void onAnimationEnd(Object animation) {
onAnimationEndCheck(false);
}
});
currentAnimation.start();
} else {
removeFragmentFromStack(currentFragment);
setVisibility(GONE);
if (backgroundView != null) {
backgroundView.setVisibility(GONE);
}
}
}
}
public void showLastFragment() {
if (fragmentsStack.isEmpty()) {
return;
}
BaseFragment previousFragment = fragmentsStack.get(fragmentsStack.size() - 1);
previousFragment.setParentLayout(this);
View fragmentView = previousFragment.createView(parentActivity.getLayoutInflater(), null);
if (previousFragment.needAddActionBar() && previousFragment.actionBar != null) {
if (removeActionBarExtraHeight) {
previousFragment.actionBar.setOccupyStatusBar(false);
}
ViewGroup parent = (ViewGroup) previousFragment.actionBar.getParent();
if (parent != null) {
parent.removeView(previousFragment.actionBar);
}
containerView.addView(previousFragment.actionBar);
previousFragment.actionBar.setTitleOverlayText(titleOverlayText);
}
containerView.addView(fragmentView);
ViewGroup.LayoutParams layoutParams = fragmentView.getLayoutParams();
layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT;
layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT;
fragmentView.setLayoutParams(layoutParams);
previousFragment.onResume();
currentActionBar = previousFragment.actionBar;
if (fragmentView.getBackground() == null) {
fragmentView.setBackgroundColor(0xffffffff);
}
}
public void removeFragmentFromStack(BaseFragment fragment) {
fragment.onPause();
fragment.onFragmentDestroy();
fragment.setParentLayout(null);
fragmentsStack.remove(fragment);
}
public void removeAllFragments() {
for (int a = 0; a < fragmentsStack.size(); a++) {
removeFragmentFromStack(fragmentsStack.get(a));
a--;
}
}
public void rebuildAllFragmentViews(boolean last) {
for (int a = 0; a < fragmentsStack.size() - (last ? 0 : 1); a++) {
fragmentsStack.get(a).setParentLayout(null);
fragmentsStack.get(a).setParentLayout(this);
}
if (delegate != null) {
delegate.onRebuildAllFragments(this);
}
}
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU && !checkTransitionAnimation() && !startedTracking && currentActionBar != null) {
currentActionBar.onMenuButtonPressed();
}
return super.onKeyUp(keyCode, event);
}
public void onActionModeStarted(ActionMode mode) {
if (currentActionBar != null) {
currentActionBar.setVisibility(GONE);
}
inActionMode = true;
}
public void onActionModeFinished(ActionMode mode) {
if (currentActionBar != null) {
currentActionBar.setVisibility(VISIBLE);
}
inActionMode = false;
}
private void onCloseAnimationEnd(boolean post) {
if (transitionAnimationInProgress && onCloseAnimationEndRunnable != null) {
transitionAnimationInProgress = false;
transitionAnimationStartTime = 0;
if (post) {
new Handler().post(new Runnable() {
public void run() {
onCloseAnimationEndRunnable.run();
onCloseAnimationEndRunnable = null;
}
});
} else {
onCloseAnimationEndRunnable.run();
onCloseAnimationEndRunnable = null;
}
}
}
private void onOpenAnimationEnd(boolean post) {
if (transitionAnimationInProgress && onOpenAnimationEndRunnable != null) {
transitionAnimationInProgress = false;
transitionAnimationStartTime = 0;
if (post) {
new Handler().post(new Runnable() {
public void run() {
onOpenAnimationEndRunnable.run();
onOpenAnimationEndRunnable = null;
}
});
} else {
onOpenAnimationEndRunnable.run();
onOpenAnimationEndRunnable = null;
}
}
}
public void startActivityForResult(final Intent intent, final int requestCode) {
if (parentActivity == null) {
return;
}
if (transitionAnimationInProgress) {
if (currentAnimation != null) {
currentAnimation.cancel();
currentAnimation = null;
}
if (onCloseAnimationEndRunnable != null) {
onCloseAnimationEnd(false);
} else if (onOpenAnimationEndRunnable != null) {
onOpenAnimationEnd(false);
}
containerView.invalidate();
if (intent != null) {
parentActivity.startActivityForResult(intent, requestCode);
}
} else {
if (intent != null) {
parentActivity.startActivityForResult(intent, requestCode);
}
}
}
public void setUseAlphaAnimations(boolean value) {
useAlphaAnimations = value;
}
public void setBackgroundView(View view) {
backgroundView = view;
}
public void setDrawerLayoutContainer(DrawerLayoutContainer layout) {
drawerLayoutContainer = layout;
}
public DrawerLayoutContainer getDrawerLayoutContainer() {
return drawerLayoutContainer;
}
public void setRemoveActionBarExtraHeight(boolean value) {
removeActionBarExtraHeight = value;
}
public void setTitleOverlayText(String text) {
titleOverlayText = text;
for (BaseFragment fragment : fragmentsStack) {
if (fragment.actionBar != null) {
fragment.actionBar.setTitleOverlayText(titleOverlayText);
}
}
}
}

View File

@ -6,9 +6,10 @@
* Copyright Nikolai Kudashov, 2013-2014.
*/
package org.telegram.ui.Views.ActionBar;
package org.telegram.ui.ActionBar;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@ -20,14 +21,12 @@ import org.telegram.android.AndroidUtilities;
public class ActionBarMenu extends LinearLayout {
private ActionBar parentActionBar;
private ActionBarLayer parentActionBarLayer;
protected ActionBar parentActionBar;
public ActionBarMenu(Context context, ActionBar actionBar, ActionBarLayer layer) {
public ActionBarMenu(Context context, ActionBar layer) {
super(context);
setOrientation(LinearLayout.HORIZONTAL);
parentActionBar = actionBar;
parentActionBarLayer = layer;
parentActionBar = layer;
}
public ActionBarMenu(Context context) {
@ -49,7 +48,7 @@ public class ActionBarMenu extends LinearLayout {
addView(view);
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)view.getLayoutParams();
layoutParams.height = FrameLayout.LayoutParams.FILL_PARENT;
view.setBackgroundResource(parentActionBarLayer.itemsBackgroundResourceId);
view.setBackgroundResource(parentActionBar.itemsBackgroundResourceId);
view.setLayoutParams(layoutParams);
view.setOnClickListener(new OnClickListener() {
@Override
@ -60,30 +59,46 @@ public class ActionBarMenu extends LinearLayout {
return view;
}
public ActionBarMenuItem addItem(int id, Drawable drawable) {
return addItem(id, 0, parentActionBar.itemsBackgroundResourceId, drawable, AndroidUtilities.dp(48));
}
public ActionBarMenuItem addItem(int id, int icon) {
return addItem(id, icon, parentActionBarLayer.itemsBackgroundResourceId);
return addItem(id, icon, parentActionBar.itemsBackgroundResourceId);
}
public ActionBarMenuItem addItem(int id, int icon, int backgroundResource) {
ActionBarMenuItem menuItem = new ActionBarMenuItem(getContext(), this, parentActionBar, backgroundResource);
return addItem(id, icon, backgroundResource, null, AndroidUtilities.dp(48));
}
public ActionBarMenuItem addItemWithWidth(int id, int icon, int width) {
return addItem(id, icon, parentActionBar.itemsBackgroundResourceId, null, width);
}
public ActionBarMenuItem addItem(int id, int icon, int backgroundResource, Drawable drawable, int width) {
ActionBarMenuItem menuItem = new ActionBarMenuItem(getContext(), this, backgroundResource);
menuItem.setTag(id);
menuItem.setScaleType(ImageView.ScaleType.CENTER);
if (drawable != null) {
menuItem.setImageDrawable(drawable);
} else {
menuItem.setImageResource(icon);
}
addView(menuItem);
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)menuItem.getLayoutParams();
layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT;
layoutParams.width = AndroidUtilities.dp(56);
layoutParams.width = width;
menuItem.setLayoutParams(layoutParams);
menuItem.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
ActionBarMenuItem item = (ActionBarMenuItem)view;
if (item.hasSubMenu()) {
if (parentActionBarLayer.actionBarMenuOnItemClick.canOpenMenu()) {
if (parentActionBar.actionBarMenuOnItemClick.canOpenMenu()) {
item.toggleSubMenu();
}
} else if (item.isSearchField()) {
parentActionBarLayer.onSearchFieldVisibilityChanged(item.toggleSearch());
parentActionBar.onSearchFieldVisibilityChanged(item.toggleSearch());
} else {
onItemClick((Integer)view.getTag());
}
@ -102,8 +117,8 @@ public class ActionBarMenu extends LinearLayout {
}
public void onItemClick(int id) {
if (parentActionBarLayer.actionBarMenuOnItemClick != null) {
parentActionBarLayer.actionBarMenuOnItemClick.onItemClick(id);
if (parentActionBar.actionBarMenuOnItemClick != null) {
parentActionBar.actionBarMenuOnItemClick.onItemClick(id);
}
}
@ -119,8 +134,9 @@ public class ActionBarMenu extends LinearLayout {
View view = getChildAt(a);
if (view instanceof ActionBarMenuItem) {
ActionBarMenuItem item = (ActionBarMenuItem)view;
if (item.hasSubMenu()) {
if (item.hasSubMenu() && item.getVisibility() == VISIBLE) {
item.toggleSubMenu();
break;
}
}
}
@ -132,7 +148,7 @@ public class ActionBarMenu extends LinearLayout {
if (view instanceof ActionBarMenuItem) {
ActionBarMenuItem item = (ActionBarMenuItem)view;
if (item.isSearchField()) {
parentActionBarLayer.onSearchFieldVisibilityChanged(item.toggleSearch());
parentActionBar.onSearchFieldVisibilityChanged(item.toggleSearch());
}
}
}

View File

@ -6,14 +6,13 @@
* Copyright Nikolai Kudashov, 2013-2014.
*/
package org.telegram.ui.Views.ActionBar;
package org.telegram.ui.ActionBar;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.Gravity;
@ -47,36 +46,39 @@ public class ActionBarMenuItem extends ImageView {
private ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout;
private ActionBarMenu parentMenu;
private ActionBarPopupWindow popupWindow;
private ActionBar parentActionBar;
private EditText searchField;
private boolean isSearchField = false;
private ActionBarMenuItemSearchListener listener;
private Rect rect = null;
private int[] location = null;
private View selectedMenuView = null;
private Rect rect;
private int[] location;
private View selectedMenuView;
private Runnable showMenuRunnable;
private boolean showFromBottom;
private int menuHeight = AndroidUtilities.dp(16);
private boolean needOffset = Build.VERSION.SDK_INT >= 21;
public ActionBarMenuItem(Context context, ActionBarMenu menu, ActionBar actionBar, int background) {
public ActionBarMenuItem(Context context, ActionBarMenu menu, int background) {
super(context);
setBackgroundResource(background);
parentMenu = menu;
parentActionBar = actionBar;
}
public ActionBarMenuItem(Context context) {
super(context);
}
public ActionBarMenuItem(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ActionBarMenuItem(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
if (hasSubMenu() && (popupWindow == null || popupWindow != null && !popupWindow.isShowing())) {
showMenuRunnable = new Runnable() {
@Override
public void run() {
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
toggleSubMenu();
}
};
AndroidUtilities.runOnUIThread(showMenuRunnable, 200);
}
} else if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
if (hasSubMenu() && (popupWindow == null || popupWindow != null && !popupWindow.isShowing())) {
if (event.getY() > getHeight()) {
if (getParent() != null) {
@ -98,9 +100,18 @@ public class ActionBarMenuItem extends ImageView {
child.getHitRect(rect);
if ((Integer)child.getTag() < 100) {
if (!rect.contains((int)x, (int)y)) {
child.setPressed(false);
child.setSelected(false);
if (Build.VERSION.SDK_INT >= 21) {
child.getBackground().setVisible(false, false);
}
} else {
child.setPressed(true);
child.setSelected(true);
if (Build.VERSION.SDK_INT >= 21) {
child.getBackground().setVisible(true, false);
child.drawableHotspotChanged(x, y - child.getTop());
}
selectedMenuView = child;
}
}
@ -121,7 +132,15 @@ public class ActionBarMenuItem extends ImageView {
return super.onTouchEvent(event);
}
public void addSubItem(int id, String text, int icon) {
public void setShowFromBottom(boolean value) {
showFromBottom = value;
}
public void setNeedOffset(boolean value) {
needOffset = Build.VERSION.SDK_INT >= 21 && value;
}
public TextView addSubItem(int id, String text, int icon) {
if (popupLayout == null) {
rect = new Rect();
location = new int[2];
@ -151,18 +170,8 @@ public class ActionBarMenuItem extends ImageView {
}
});
}
if (popupLayout.getChildCount() != 0) {
View delimeter = new View(getContext());
delimeter.setBackgroundColor(0xffdcdcdc);
popupLayout.addView(delimeter);
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)delimeter.getLayoutParams();
layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT;
layoutParams.height = AndroidUtilities.density >= 3 ? 2 : 1;
delimeter.setLayoutParams(layoutParams);
delimeter.setTag(100 + id);
}
TextView textView = new TextView(getContext());
textView.setTextColor(0xff000000);
textView.setTextColor(0xff212121);
textView.setBackgroundResource(R.drawable.list_selector);
if (!LocaleController.isRTL) {
textView.setGravity(Gravity.CENTER_VERTICAL);
@ -187,7 +196,7 @@ public class ActionBarMenuItem extends ImageView {
if (LocaleController.isRTL) {
layoutParams.gravity = Gravity.RIGHT;
}
layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT;
layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT;
layoutParams.height = AndroidUtilities.dp(48);
textView.setLayoutParams(layoutParams);
textView.setOnClickListener(new OnClickListener() {
@ -199,6 +208,8 @@ public class ActionBarMenuItem extends ImageView {
}
}
});
menuHeight += layoutParams.height;
return textView;
}
public boolean hasSubMenu() {
@ -206,6 +217,13 @@ public class ActionBarMenuItem extends ImageView {
}
public void toggleSubMenu() {
if (popupLayout == null) {
return;
}
if (showMenuRunnable != null) {
AndroidUtilities.cancelRunOnUIThread(showMenuRunnable);
showMenuRunnable = null;
}
if (popupWindow != null && popupWindow.isShowing()) {
popupWindow.dismiss();
return;
@ -213,6 +231,7 @@ public class ActionBarMenuItem extends ImageView {
if (popupWindow == null) {
popupWindow = new ActionBarPopupWindow(popupLayout, FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
//popupWindow.setBackgroundDrawable(new BitmapDrawable());
popupWindow.setAnimationStyle(R.style.PopupAnimation);
popupWindow.setOutsideTouchable(true);
popupWindow.setClippingEnabled(true);
popupWindow.setInputMethodMode(ActionBarPopupWindow.INPUT_METHOD_NOT_NEEDED);
@ -232,10 +251,33 @@ public class ActionBarMenuItem extends ImageView {
}
popupWindow.setFocusable(true);
if (popupLayout.getMeasuredWidth() == 0) {
popupWindow.showAsDropDown(this, parentActionBar.getMeasuredWidth() - popupLayout.getMeasuredWidth() - getLeft() - parentMenu.getLeft(), 0);
popupWindow.update(this, parentActionBar.getMeasuredWidth() - popupLayout.getMeasuredWidth() - getLeft() - parentMenu.getLeft(), 0, -1, -1);
if (showFromBottom) {
popupWindow.showAsDropDown(this, -popupLayout.getMeasuredWidth() + getMeasuredWidth() + AndroidUtilities.dp(14), getOffsetY());
popupWindow.update(this, -popupLayout.getMeasuredWidth() + getMeasuredWidth() + AndroidUtilities.dp(14), getOffsetY(), -1, -1);
} else {
popupWindow.showAsDropDown(this, parentActionBar.getMeasuredWidth() - popupLayout.getMeasuredWidth() - getLeft() - parentMenu.getLeft(), 0);
popupWindow.showAsDropDown(this, parentMenu.parentActionBar.getMeasuredWidth() - popupLayout.getMeasuredWidth() - getLeft() - parentMenu.getLeft(), getOffsetY());
popupWindow.update(this, parentMenu.parentActionBar.getMeasuredWidth() - popupLayout.getMeasuredWidth() - getLeft() - parentMenu.getLeft(), getOffsetY(), -1, -1);
}
} else {
if (showFromBottom) {
popupWindow.showAsDropDown(this, -popupLayout.getMeasuredWidth() + getMeasuredWidth() + AndroidUtilities.dp(14), getOffsetY());
} else {
popupWindow.showAsDropDown(this, parentMenu.parentActionBar.getMeasuredWidth() - popupLayout.getMeasuredWidth() - getLeft() - parentMenu.getLeft(), getOffsetY());
}
}
}
private int getOffsetY() {
if (showFromBottom) {
getLocationOnScreen(location);
int diff = location[1] - AndroidUtilities.statusBarHeight + getMeasuredHeight() - menuHeight;
int y = AndroidUtilities.dp(8) - menuHeight;
if (diff < 0) {
y -= diff;
}
return y - (needOffset ? AndroidUtilities.statusBarHeight : 0);
} else {
return -getMeasuredHeight() - (needOffset ? AndroidUtilities.statusBarHeight : 0);
}
}
@ -281,7 +323,7 @@ public class ActionBarMenuItem extends ImageView {
searchField.setTextColor(0xffffffff);
searchField.setSingleLine(true);
searchField.setBackgroundResource(R.drawable.search_light_states);
searchField.setPadding(AndroidUtilities.dp(6), 0, AndroidUtilities.dp(6), 0);
searchField.setPadding(0, 0, 0, 0);
searchField.setInputType(EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
if (android.os.Build.VERSION.SDK_INT < 11) {
searchField.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
@ -360,7 +402,8 @@ public class ActionBarMenuItem extends ImageView {
layoutParams.width = 0;
layoutParams.gravity = Gravity.CENTER_VERTICAL;
layoutParams.height = AndroidUtilities.dp(36);
layoutParams.rightMargin = AndroidUtilities.dp(4);
layoutParams.rightMargin = AndroidUtilities.dp(22);
layoutParams.leftMargin = AndroidUtilities.dp(6);
searchField.setLayoutParams(layoutParams);
searchField.setVisibility(GONE);
}
@ -377,10 +420,14 @@ public class ActionBarMenuItem extends ImageView {
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (popupWindow != null && popupWindow.isShowing()) {
popupWindow.update(this, parentActionBar.getMeasuredWidth() - popupLayout.getMeasuredWidth() - getLeft() - parentMenu.getLeft(), 0, -1, -1);
if (showFromBottom) {
popupWindow.update(this, -popupLayout.getMeasuredWidth() + getMeasuredWidth() + AndroidUtilities.dp(14), getOffsetY(), -1, -1);
} else {
popupWindow.update(this, parentMenu.parentActionBar.getMeasuredWidth() - popupLayout.getMeasuredWidth() - getLeft() - parentMenu.getLeft(), getOffsetY(), -1, -1);
}
}
}

View File

@ -8,19 +8,24 @@
//Thanks to https://github.com/JakeWharton/ActionBarSherlock/
package org.telegram.ui.Views.ActionBar;
package org.telegram.ui.ActionBar;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import org.telegram.messenger.FileLog;
import java.lang.reflect.Field;
public class ActionBarPopupWindow extends PopupWindow {
private static final Field superListenerField;
static {
Field f = null;
@ -130,6 +135,15 @@ public class ActionBarPopupWindow extends PopupWindow {
mSuperScrollListener = null;
}
}
if (Build.VERSION.SDK_INT >= 21) {
try {
Field field = PopupWindow.class.getDeclaredField("mWindowLayoutType");
field.setAccessible(true);
field.set(this, WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
} catch (Exception e) {
/* ignored */
}
}
}
private void unregisterListener() {
@ -157,8 +171,12 @@ public class ActionBarPopupWindow extends PopupWindow {
@Override
public void showAsDropDown(View anchor, int xoff, int yoff) {
try {
super.showAsDropDown(anchor, xoff, yoff);
registerListener(anchor);
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
@Override

View File

@ -6,7 +6,7 @@
* Copyright Nikolai Kudashov, 2013.
*/
package org.telegram.ui.Views.ActionBar;
package org.telegram.ui.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
@ -23,12 +23,13 @@ import org.telegram.messenger.R;
public class BaseFragment {
private boolean isFinished = false;
private AlertDialog visibleDialog = null;
protected View fragmentView;
protected ActionBarLayout parentLayout;
protected ActionBarLayer actionBarLayer;
protected ActionBar actionBar;
protected int classGuid = 0;
protected Bundle arguments;
private AlertDialog visibleDialog = null;
protected boolean swipeBackEnabled = true;
public BaseFragment() {
@ -48,7 +49,7 @@ public class BaseFragment {
return arguments;
}
public void setParentLayout(ActionBarLayout layout) {
protected void setParentLayout(ActionBarLayout layout) {
if (parentLayout != layout) {
parentLayout = layout;
if (fragmentView != null) {
@ -62,14 +63,21 @@ public class BaseFragment {
}
fragmentView = null;
}
if (parentLayout != null) {
if (actionBarLayer != null) {
actionBarLayer.onDestroy();
if (actionBar != null) {
ViewGroup parent = (ViewGroup) actionBar.getParent();
if (parent != null) {
try {
parent.removeView(actionBar);
} catch (Exception e) {
FileLog.e("tmessages", e);
}
actionBarLayer = parentLayout.getInternalActionBar().createLayer();
actionBarLayer.parentFragment = this;
actionBarLayer.setBackgroundResource(R.color.header);
actionBarLayer.setItemsBackground(R.drawable.bar_selector);
}
}
if (parentLayout != null) {
actionBar = new ActionBar(parentLayout.getContext());
actionBar.parentFragment = this;
actionBar.setBackgroundResource(R.color.header);
actionBar.setItemsBackground(R.drawable.bar_selector);
}
}
}
@ -99,8 +107,8 @@ public class BaseFragment {
public void onFragmentDestroy() {
ConnectionsManager.getInstance().cancelRpcsForClassGuid(classGuid);
isFinished = true;
if (actionBarLayer != null) {
actionBarLayer.setEnabled(false);
if (actionBar != null) {
actionBar.setEnabled(false);
}
}
@ -109,9 +117,8 @@ public class BaseFragment {
}
public void onPause() {
if (actionBarLayer != null) {
actionBarLayer.onPause();
actionBarLayer.closeSearchField();
if (actionBar != null) {
actionBar.onPause();
}
try {
if (visibleDialog != null && visibleDialog.isShowing()) {
@ -168,18 +175,6 @@ public class BaseFragment {
}
}
public void showActionBar() {
if (parentLayout != null) {
parentLayout.showActionBar();
}
}
public void hideActionBar() {
if (parentLayout != null) {
parentLayout.hideActionBar();
}
}
public void onBeginSlide() {
try {
if (visibleDialog != null && visibleDialog.isShowing()) {
@ -189,8 +184,8 @@ public class BaseFragment {
} catch (Exception e) {
FileLog.e("tmessages", e);
}
if (actionBarLayer != null) {
actionBarLayer.onPause();
if (actionBar != null) {
actionBar.onPause();
}
}
@ -202,6 +197,10 @@ public class BaseFragment {
}
public boolean needAddActionBar() {
return true;
}
protected void showAlertDialog(AlertDialog.Builder builder) {
if (parentLayout == null || parentLayout.checkTransitionAnimation() || parentLayout.animationInProgress || parentLayout.startedTracking) {
return;
@ -214,6 +213,7 @@ public class BaseFragment {
} catch (Exception e) {
FileLog.e("tmessages", e);
}
try {
visibleDialog = builder.show();
visibleDialog.setCanceledOnTouchOutside(true);
visibleDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@ -223,6 +223,9 @@ public class BaseFragment {
onDialogDismiss();
}
});
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
protected void onDialogDismiss() {

View File

@ -0,0 +1,477 @@
/*
* This is the source code of Telegram for Android v. 1.7.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-2014.
*/
package org.telegram.ui.ActionBar;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ListView;
import org.telegram.android.AndroidUtilities;
import org.telegram.messenger.R;
import org.telegram.ui.AnimationCompat.AnimatorListenerAdapterProxy;
import org.telegram.ui.AnimationCompat.AnimatorSetProxy;
import org.telegram.ui.AnimationCompat.ObjectAnimatorProxy;
public class DrawerLayoutContainer extends FrameLayout {
private static final int MIN_DRAWER_MARGIN = 64;
private ViewGroup drawerLayout;
private ActionBarLayout parentActionBarLayout;
private boolean maybeStartTracking = false;
private boolean startedTracking = false;
private int startedTrackingX;
private int startedTrackingY;
private int startedTrackingPointerId;
private VelocityTracker velocityTracker = null;
private boolean beginTrackingSent;
private AnimatorSetProxy currentAnimation = null;
private Paint scrimPaint = new Paint();
private Object lastInsets;
private boolean inLayout;
private int minDrawerMargin;
private float scrimOpacity;
private Drawable shadowLeft;
private boolean allowOpenDrawer;
private float drawerPosition = 0;
private boolean drawerOpened = false;
public DrawerLayoutContainer(Context context) {
super(context);
minDrawerMargin = (int) (MIN_DRAWER_MARGIN * AndroidUtilities.density + 0.5f);
setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
setFocusableInTouchMode(true);
if (Build.VERSION.SDK_INT >= 21) {
setFitsSystemWindows(true);
configureApplyInsets(this);
}
shadowLeft = getResources().getDrawable(R.drawable.menu_shadow);
}
private class InsetsListener implements View.OnApplyWindowInsetsListener {
@Override
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
final DrawerLayoutContainer drawerLayout = (DrawerLayoutContainer) v;
drawerLayout.setChildInsets(insets, insets.getSystemWindowInsetTop() > 0);
return insets.consumeSystemWindowInsets();
}
}
private void configureApplyInsets(View drawerLayout) {
if (Build.VERSION.SDK_INT >= 21) {
drawerLayout.setOnApplyWindowInsetsListener(new InsetsListener());
drawerLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
}
private void dispatchChildInsets(View child, Object insets, int drawerGravity) {
WindowInsets wi = (WindowInsets) insets;
if (drawerGravity == Gravity.LEFT) {
wi = wi.replaceSystemWindowInsets(wi.getSystemWindowInsetLeft(), wi.getSystemWindowInsetTop(), 0, wi.getSystemWindowInsetBottom());
} else if (drawerGravity == Gravity.RIGHT) {
wi = wi.replaceSystemWindowInsets(0, wi.getSystemWindowInsetTop(), wi.getSystemWindowInsetRight(), wi.getSystemWindowInsetBottom());
}
child.dispatchApplyWindowInsets(wi);
}
private void applyMarginInsets(MarginLayoutParams lp, Object insets, int drawerGravity, boolean topOnly) {
WindowInsets wi = (WindowInsets) insets;
if (drawerGravity == Gravity.LEFT) {
wi = wi.replaceSystemWindowInsets(wi.getSystemWindowInsetLeft(), wi.getSystemWindowInsetTop(), 0, wi.getSystemWindowInsetBottom());
} else if (drawerGravity == Gravity.RIGHT) {
wi = wi.replaceSystemWindowInsets(0, wi.getSystemWindowInsetTop(), wi.getSystemWindowInsetRight(), wi.getSystemWindowInsetBottom());
}
lp.leftMargin = wi.getSystemWindowInsetLeft();
lp.topMargin = topOnly ? 0 : wi.getSystemWindowInsetTop();
lp.rightMargin = wi.getSystemWindowInsetRight();
lp.bottomMargin = wi.getSystemWindowInsetBottom();
}
private int getTopInset(Object insets) {
if (Build.VERSION.SDK_INT >= 21) {
return insets != null ? ((WindowInsets) insets).getSystemWindowInsetTop() : 0;
}
return 0;
}
private void setChildInsets(Object insets, boolean draw) {
lastInsets = insets;
setWillNotDraw(!draw && getBackground() == null);
requestLayout();
}
public void setDrawerLayout(ViewGroup layout) {
drawerLayout = layout;
addView(drawerLayout);
if (Build.VERSION.SDK_INT >= 21) {
drawerLayout.setFitsSystemWindows(true);
}
}
public void moveDrawerByX(float dx) {
setDrawerPosition(drawerPosition + dx);
}
public void setDrawerPosition(float value) {
drawerPosition = value;
if (drawerPosition > drawerLayout.getMeasuredWidth()) {
drawerPosition = drawerLayout.getMeasuredWidth();
} else if (drawerPosition < 0) {
drawerPosition = 0;
}
requestLayout();
final int newVisibility = drawerPosition > 0 ? VISIBLE : INVISIBLE;
if (drawerLayout.getVisibility() != newVisibility) {
drawerLayout.setVisibility(newVisibility);
}
setScrimOpacity(drawerPosition / (float)drawerLayout.getMeasuredWidth());
}
public float getDrawerPosition() {
return drawerPosition;
}
public void cancelCurrentAnimation() {
if (currentAnimation != null) {
currentAnimation.cancel();
currentAnimation = null;
}
}
public void openDrawer(boolean fast) {
if (AndroidUtilities.isTablet() && parentActionBarLayout != null && parentActionBarLayout.parentActivity != null) {
AndroidUtilities.hideKeyboard(parentActionBarLayout.parentActivity.getCurrentFocus());
}
cancelCurrentAnimation();
AnimatorSetProxy animatorSet = new AnimatorSetProxy();
animatorSet.playTogether(
ObjectAnimatorProxy.ofFloat(this, "drawerPosition", drawerLayout.getMeasuredWidth())
);
animatorSet.setInterpolator(new DecelerateInterpolator());
if (fast) {
animatorSet.setDuration(Math.max((int) (200.0f / drawerLayout.getMeasuredWidth() * (drawerLayout.getMeasuredWidth() - drawerPosition)), 50));
} else {
animatorSet.setDuration(300);
}
animatorSet.addListener(new AnimatorListenerAdapterProxy() {
@Override
public void onAnimationEnd(Object animator) {
onDrawerAnimationEnd(true);
}
@Override
public void onAnimationCancel(Object animator) {
onDrawerAnimationEnd(true);
}
});
animatorSet.start();
currentAnimation = animatorSet;
}
public void closeDrawer(boolean fast) {
cancelCurrentAnimation();
AnimatorSetProxy animatorSet = new AnimatorSetProxy();
animatorSet.playTogether(
ObjectAnimatorProxy.ofFloat(this, "drawerPosition", 0)
);
animatorSet.setInterpolator(new DecelerateInterpolator());
if (fast) {
animatorSet.setDuration(Math.max((int) (200.0f / drawerLayout.getMeasuredWidth() * drawerPosition), 50));
} else {
animatorSet.setDuration(300);
}
animatorSet.addListener(new AnimatorListenerAdapterProxy() {
@Override
public void onAnimationEnd(Object animator) {
onDrawerAnimationEnd(false);
}
@Override
public void onAnimationCancel(Object animator) {
onDrawerAnimationEnd(false);
}
});
animatorSet.start();
}
private void onDrawerAnimationEnd(boolean opened) {
startedTracking = false;
currentAnimation = null;
drawerOpened = opened;
if (!opened) {
if (drawerLayout instanceof ListView) {
((ListView)drawerLayout).setSelectionFromTop(0, 0);
}
}
}
private void setScrimOpacity(float value) {
scrimOpacity = value;
invalidate();
}
private float getScrimOpacity() {
return scrimOpacity;
}
public View getDrawerLayout() {
return drawerLayout;
}
public void setParentActionBarLayout(ActionBarLayout layout) {
parentActionBarLayout = layout;
}
public void setAllowOpenDrawer(boolean value) {
allowOpenDrawer = value;
if (!allowOpenDrawer && drawerPosition != 0) {
setDrawerPosition(0);
onDrawerAnimationEnd(false);
}
}
private void prepareForDrawerOpen(MotionEvent ev) {
maybeStartTracking = false;
startedTracking = true;
if (ev != null) {
startedTrackingX = (int) ev.getX();
}
beginTrackingSent = false;
}
public boolean isDrawerOpened() {
return drawerOpened;
}
public boolean onTouchEvent(MotionEvent ev) {
if (!parentActionBarLayout.checkTransitionAnimation()) {
if (drawerOpened && ev != null && ev.getX() > drawerPosition && !startedTracking) {
if (ev.getAction() == MotionEvent.ACTION_UP) {
closeDrawer(false);
}
return true;
}
if (allowOpenDrawer && parentActionBarLayout.fragmentsStack.size() == 1) {
if (ev != null && (ev.getAction() == MotionEvent.ACTION_DOWN || ev.getAction() == MotionEvent.ACTION_MOVE) && !startedTracking && !maybeStartTracking) {
startedTrackingPointerId = ev.getPointerId(0);
maybeStartTracking = true;
startedTrackingX = (int) ev.getX();
startedTrackingY = (int) ev.getY();
cancelCurrentAnimation();
if (velocityTracker != null) {
velocityTracker.clear();
}
} else if (ev != null && ev.getAction() == MotionEvent.ACTION_MOVE && ev.getPointerId(0) == startedTrackingPointerId) {
if (velocityTracker == null) {
velocityTracker = VelocityTracker.obtain();
}
float dx = (int) (ev.getX() - startedTrackingX);
float dy = Math.abs((int) ev.getY() - startedTrackingY);
velocityTracker.addMovement(ev);
if (maybeStartTracking && !startedTracking && (dx > 0 && dx / 3.0f > Math.abs(dy) || dx < 0 && Math.abs(dx) >= Math.abs(dy) && Math.abs(dx) >= AndroidUtilities.dp(10))) {
prepareForDrawerOpen(ev);
startedTrackingX = (int) ev.getX();
requestDisallowInterceptTouchEvent(true);
} else if (startedTracking) {
if (!beginTrackingSent) {
if (((Activity)getContext()).getCurrentFocus() != null) {
AndroidUtilities.hideKeyboard(((Activity)getContext()).getCurrentFocus());
}
beginTrackingSent = true;
}
moveDrawerByX(dx);
startedTrackingX = (int) ev.getX();
}
} else if (ev == null || ev != null && ev.getPointerId(0) == startedTrackingPointerId && (ev.getAction() == MotionEvent.ACTION_CANCEL || ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_POINTER_UP)) {
if (velocityTracker == null) {
velocityTracker = VelocityTracker.obtain();
}
velocityTracker.computeCurrentVelocity(1000);
/*if (!startedTracking) {
float velX = velocityTracker.getXVelocity();
float velY = velocityTracker.getYVelocity();
if (Math.abs(velX) >= 3500 && Math.abs(velX) > Math.abs(velY)) {
prepareForDrawerOpen(ev);
if (!beginTrackingSent) {
if (((Activity)getContext()).getCurrentFocus() != null) {
AndroidUtilities.hideKeyboard(((Activity)getContext()).getCurrentFocus());
}
beginTrackingSent = true;
}
}
}*/
if (startedTracking || drawerPosition != 0 && drawerPosition != drawerLayout.getMeasuredWidth()) {
float velX = velocityTracker.getXVelocity();
float velY = velocityTracker.getYVelocity();
boolean backAnimation = drawerPosition < drawerLayout.getMeasuredWidth() / 2.0f && (velX < 3500 || Math.abs(velX) < Math.abs(velY)) || velX < 0 && Math.abs(velX) >= 3500;
if (!backAnimation) {
openDrawer(!drawerOpened && Math.abs(velX) >= 3500);
} else {
closeDrawer(drawerOpened && Math.abs(velX) >= 3500);
}
startedTracking = false;
} else {
maybeStartTracking = false;
startedTracking = false;
}
if (velocityTracker != null) {
velocityTracker.recycle();
velocityTracker = null;
}
}
}
return startedTracking;
}
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return parentActionBarLayout.checkTransitionAnimation() || onTouchEvent(ev);
}
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (maybeStartTracking && !startedTracking) {
onTouchEvent(null);
}
super.requestDisallowInterceptTouchEvent(disallowIntercept);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
inLayout = true;
final int width = r - l;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (drawerLayout != child) {
child.layout(lp.leftMargin, lp.topMargin, lp.leftMargin + child.getMeasuredWidth(), lp.topMargin + child.getMeasuredHeight());
} else {
child.layout(-child.getMeasuredWidth() + (int)drawerPosition, lp.topMargin, (int)drawerPosition, lp.topMargin + child.getMeasuredHeight());
}
}
inLayout = false;
}
@Override
public void requestLayout() {
if (!inLayout) {
super.requestLayout();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthSize, heightSize);
final boolean applyInsets = lastInsets != null && Build.VERSION.SDK_INT >= 21;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (applyInsets) {
if (child.getFitsSystemWindows()) {
dispatchChildInsets(child, lastInsets, lp.gravity);
} else {
applyMarginInsets(lp, lastInsets, lp.gravity, Build.VERSION.SDK_INT >= 21);
}
}
if (drawerLayout != child) {
final int contentWidthSpec = MeasureSpec.makeMeasureSpec(widthSize - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY);
final int contentHeightSpec = MeasureSpec.makeMeasureSpec(heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY);
child.measure(contentWidthSpec, contentHeightSpec);
} else {
child.setPadding(0, 0, 0, 0);
final int drawerWidthSpec = getChildMeasureSpec(widthMeasureSpec, minDrawerMargin + lp.leftMargin + lp.rightMargin, lp.width);
final int drawerHeightSpec = getChildMeasureSpec(heightMeasureSpec, lp.topMargin + lp.bottomMargin, lp.height);
child.measure(drawerWidthSpec, drawerHeightSpec);
}
}
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
final int height = getHeight();
final boolean drawingContent = child != drawerLayout;
int clipLeft = 0, clipRight = getWidth();
final int restoreCount = canvas.save();
if (drawingContent) {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View v = getChildAt(i);
if (v == child || v.getVisibility() != VISIBLE || v != drawerLayout || v.getHeight() < height) {
continue;
}
final int vright = v.getRight();
if (vright > clipLeft) {
clipLeft = vright;
}
}
canvas.clipRect(clipLeft, 0, clipRight, getHeight());
}
final boolean result = super.drawChild(canvas, child, drawingTime);
canvas.restoreToCount(restoreCount);
if (scrimOpacity > 0 && drawingContent) {
scrimPaint.setColor((int) (((0x99000000 & 0xff000000) >>> 24) * scrimOpacity) << 24);
canvas.drawRect(clipLeft, 0, clipRight, getHeight(), scrimPaint);
} else if (shadowLeft != null) {
final float alpha = Math.max(0, Math.min(drawerPosition / AndroidUtilities.dp(20), 1.0f));
if (alpha != 0) {
shadowLeft.setBounds((int)drawerPosition, child.getTop(), (int)drawerPosition + shadowLeft.getIntrinsicWidth(), child.getBottom());
shadowLeft.setAlpha((int) (0xff * alpha));
shadowLeft.draw(canvas);
}
}
return result;
}
}

View File

@ -0,0 +1,116 @@
/*
* This is the source code of Telegram for Android v. 1.7.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-2014.
*/
package org.telegram.ui.ActionBar;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.view.animation.DecelerateInterpolator;
import org.telegram.android.AndroidUtilities;
public class MenuDrawable extends Drawable {
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private boolean reverseAngle = false;
private long lastFrameTime;
private boolean animationInProgress;
private float finalRotation;
private float currentRotation;
private int currentAnimationTime;
private DecelerateInterpolator interpolator = new DecelerateInterpolator();
public MenuDrawable() {
super();
paint.setColor(0xffffffff);
paint.setStrokeWidth(AndroidUtilities.dp(2));
}
public void setRotation(float rotation, boolean animated) {
lastFrameTime = 0;
if (currentRotation == 1) {
reverseAngle = true;
} else if (currentRotation == 0) {
reverseAngle = false;
}
lastFrameTime = 0;
if (animated) {
if (currentRotation < rotation) {
currentAnimationTime = (int) (currentRotation * 300);
} else {
currentAnimationTime = (int) ((1.0f - currentRotation) * 300);
}
lastFrameTime = System.currentTimeMillis();
finalRotation = rotation;
} else {
finalRotation = currentRotation = rotation;
}
invalidateSelf();
}
@Override
public void draw(Canvas canvas) {
if (currentRotation != finalRotation) {
if (lastFrameTime != 0) {
long dt = System.currentTimeMillis() - lastFrameTime;
currentAnimationTime += dt;
if (currentAnimationTime >= 300) {
currentRotation = finalRotation;
} else {
if (currentRotation < finalRotation) {
currentRotation = interpolator.getInterpolation(currentAnimationTime / 300.0f) * finalRotation;
} else {
currentRotation = 1.0f - interpolator.getInterpolation(currentAnimationTime / 300.0f);
}
}
}
lastFrameTime = System.currentTimeMillis();
invalidateSelf();
}
canvas.save();
canvas.translate(getIntrinsicWidth() / 2, getIntrinsicHeight() / 2);
canvas.rotate(currentRotation * (reverseAngle ? -180 : 180));
canvas.drawLine(-AndroidUtilities.dp(9), 0, AndroidUtilities.dp(9) - AndroidUtilities.dp(1) * currentRotation, 0, paint);
float endYDiff = AndroidUtilities.dp(5) * (1 - Math.abs(currentRotation)) - AndroidUtilities.dp(0.5f) * Math.abs(currentRotation);
float endXDiff = AndroidUtilities.dp(9) - AndroidUtilities.dp(0.5f) * Math.abs(currentRotation);
float startYDiff = AndroidUtilities.dp(5) + AndroidUtilities.dp(3.5f) * Math.abs(currentRotation);
float startXDiff = -AndroidUtilities.dp(9) + AndroidUtilities.dp(8.5f) * Math.abs(currentRotation);
canvas.drawLine(startXDiff, -startYDiff, endXDiff, -endYDiff, paint);
canvas.drawLine(startXDiff, startYDiff, endXDiff, endYDiff, paint);
canvas.restore();
}
@Override
public void setAlpha(int alpha) {
}
@Override
public void setColorFilter(ColorFilter cf) {
}
@Override
public int getOpacity() {
return 0;
}
@Override
public int getIntrinsicWidth() {
return AndroidUtilities.dp(24);
}
@Override
public int getIntrinsicHeight() {
return AndroidUtilities.dp(24);
}
}

View File

@ -24,11 +24,11 @@ public class BaseContactsSearchAdapter extends BaseFragmentAdapter {
protected String lastFoundUsername = null;
public void queryServerSearch(final String query) {
if (query == null || query.length() < 5) {
if (reqId != 0) {
ConnectionsManager.getInstance().cancelRpc(reqId, true);
reqId = 0;
}
if (query == null || query.length() < 5) {
globalSearch.clear();
lastReqId = 0;
notifyDataSetChanged();
@ -41,7 +41,7 @@ public class BaseContactsSearchAdapter extends BaseFragmentAdapter {
reqId = ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() {
@Override
public void run(final TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (currentReqId == lastReqId) {

View File

@ -15,12 +15,6 @@ import android.widget.BaseAdapter;
public class BaseFragmentAdapter extends BaseAdapter {
public void onFragmentCreate() {
}
public void onFragmentDestroy() {
}
@Override
public int getCount() {
return 0;

View File

@ -0,0 +1,152 @@
/*
* This is the source code of Telegram for Android v. 1.7.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-2014.
*/
package org.telegram.ui.Adapters;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
public abstract class BaseSectionsAdapter extends BaseFragmentAdapter {
private SparseArray<Integer> sectionPositionCache;
private SparseArray<Integer> sectionCache;
private SparseArray<Integer> sectionCountCache;
private int sectionCount;
private int count;
private void cleanupCache() {
sectionCache = new SparseArray<Integer>();
sectionPositionCache = new SparseArray<Integer>();
sectionCountCache = new SparseArray<Integer>();
count = -1;
sectionCount = -1;
}
public BaseSectionsAdapter() {
super();
cleanupCache();
}
@Override
public void notifyDataSetChanged() {
cleanupCache();
super.notifyDataSetChanged();
}
@Override
public void notifyDataSetInvalidated() {
cleanupCache();
super.notifyDataSetInvalidated();
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
@Override
public boolean isEnabled(int position) {
return isRowEnabled(getSectionForPosition(position), getPositionInSectionForPosition(position));
}
@Override
public final long getItemId(int position) {
return position;
}
@Override
public final int getCount() {
if (count >= 0) {
return count;
}
count = 0;
for (int i = 0; i < internalGetSectionCount(); i++) {
count += internalGetCountForSection(i);
}
return count;
}
@Override
public final Object getItem(int position) {
return getItem(getSectionForPosition(position), getPositionInSectionForPosition(position));
}
@Override
public final int getItemViewType(int position) {
return getItemViewType(getSectionForPosition(position), getPositionInSectionForPosition(position));
}
@Override
public final View getView(int position, View convertView, ViewGroup parent) {
return getItemView(getSectionForPosition(position), getPositionInSectionForPosition(position), convertView, parent);
}
private int internalGetCountForSection(int section) {
Integer cachedSectionCount = sectionCountCache.get(section);
if (cachedSectionCount != null) {
return cachedSectionCount;
}
int sectionCount = getCountForSection(section);
sectionCountCache.put(section, sectionCount);
return sectionCount;
}
private int internalGetSectionCount() {
if (sectionCount >= 0) {
return sectionCount;
}
sectionCount = getSectionCount();
return sectionCount;
}
public final int getSectionForPosition(int position) {
Integer cachedSection = sectionCache.get(position);
if (cachedSection != null) {
return cachedSection;
}
int sectionStart = 0;
for (int i = 0; i < internalGetSectionCount(); i++) {
int sectionCount = internalGetCountForSection(i);
int sectionEnd = sectionStart + sectionCount;
if (position >= sectionStart && position < sectionEnd) {
sectionCache.put(position, i);
return i;
}
sectionStart = sectionEnd;
}
return -1;
}
public int getPositionInSectionForPosition(int position) {
Integer cachedPosition = sectionPositionCache.get(position);
if (cachedPosition != null) {
return cachedPosition;
}
int sectionStart = 0;
for (int i = 0; i < internalGetSectionCount(); i++) {
int sectionCount = internalGetCountForSection(i);
int sectionEnd = sectionStart + sectionCount;
if (position >= sectionStart && position < sectionEnd) {
int positionInSection = position - sectionStart;
sectionPositionCache.put(position, positionInSection);
return positionInSection;
}
sectionStart = sectionEnd;
}
return -1;
}
public abstract int getSectionCount();
public abstract int getCountForSection(int section);
public abstract boolean isRowEnabled(int section, int row);
public abstract int getItemViewType(int section, int position);
public abstract Object getItem(int section, int position);
public abstract View getItemView(int section, int position, View convertView, ViewGroup parent);
public abstract View getSectionHeaderView(int section, View convertView, ViewGroup parent);
}

View File

@ -1,229 +0,0 @@
/*
* This is the source code of Telegram for Android v. 1.3.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-2014.
*/
package org.telegram.ui.Adapters;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.telegram.android.LocaleController;
import org.telegram.messenger.TLRPC;
import org.telegram.android.ContactsController;
import org.telegram.android.MessagesController;
import org.telegram.messenger.R;
import org.telegram.ui.Cells.ChatOrUserCell;
import org.telegram.ui.Views.SectionedBaseAdapter;
import org.telegram.ui.Views.SettingsSectionLayout;
import java.util.ArrayList;
import java.util.HashMap;
public class ContactsActivityAdapter extends SectionedBaseAdapter {
private Context mContext;
private boolean onlyUsers;
private boolean usersAsSections;
private HashMap<Integer, TLRPC.User> ignoreUsers;
public ContactsActivityAdapter(Context context, boolean arg1, boolean arg2, HashMap<Integer, TLRPC.User> arg3) {
mContext = context;
onlyUsers = arg1;
usersAsSections = arg2;
ignoreUsers = arg3;
}
@Override
public Object getItem(int section, int position) {
return null;
}
@Override
public long getItemId(int section, int position) {
return 0;
}
@Override
public int getSectionCount() {
int count = 0;
if (usersAsSections) {
count += ContactsController.getInstance().sortedUsersSectionsArray.size();
} else {
count++;
}
if (!onlyUsers) {
count += ContactsController.getInstance().sortedContactsSectionsArray.size();
}
return count;
}
@Override
public int getCountForSection(int section) {
if (usersAsSections) {
if (section < ContactsController.getInstance().sortedUsersSectionsArray.size()) {
ArrayList<TLRPC.TL_contact> arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section));
return arr.size();
}
} else {
if (section == 0) {
return ContactsController.getInstance().contacts.size() + 1;
}
}
ArrayList<ContactsController.Contact> arr = ContactsController.getInstance().contactsSectionsDict.get(ContactsController.getInstance().sortedContactsSectionsArray.get(section - 1));
return arr.size();
}
@Override
public View getItemView(int section, int position, View convertView, ViewGroup parent) {
TLRPC.User user = null;
int count = 0;
if (usersAsSections) {
if (section < ContactsController.getInstance().sortedUsersSectionsArray.size()) {
ArrayList<TLRPC.TL_contact> arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section));
user = MessagesController.getInstance().getUser(arr.get(position).user_id);
count = arr.size();
}
} else {
if (section == 0) {
if (position == 0) {
if (convertView == null) {
LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = li.inflate(R.layout.contacts_invite_row_layout, parent, false);
TextView textView = (TextView)convertView.findViewById(R.id.messages_list_row_name);
textView.setText(LocaleController.getString("InviteFriends", R.string.InviteFriends));
}
View divider = convertView.findViewById(R.id.settings_row_divider);
if (ContactsController.getInstance().contacts.isEmpty()) {
divider.setVisibility(View.INVISIBLE);
} else {
divider.setVisibility(View.VISIBLE);
}
return convertView;
}
user = MessagesController.getInstance().getUser(ContactsController.getInstance().contacts.get(position - 1).user_id);
count = ContactsController.getInstance().contacts.size();
}
}
if (user != null) {
if (convertView == null) {
convertView = new ChatOrUserCell(mContext);
((ChatOrUserCell)convertView).usePadding = false;
}
((ChatOrUserCell)convertView).setData(user, null, null, null, null);
if (ignoreUsers != null) {
if (ignoreUsers.containsKey(user.id)) {
((ChatOrUserCell)convertView).drawAlpha = 0.5f;
} else {
((ChatOrUserCell)convertView).drawAlpha = 1.0f;
}
}
((ChatOrUserCell) convertView).useSeparator = position != count - 1;
return convertView;
}
TextView textView;
if (convertView == null) {
LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = li.inflate(R.layout.settings_row_button_layout, parent, false);
textView = (TextView)convertView.findViewById(R.id.settings_row_text);
} else {
textView = (TextView)convertView.findViewById(R.id.settings_row_text);
}
View divider = convertView.findViewById(R.id.settings_row_divider);
ArrayList<ContactsController.Contact> arr = ContactsController.getInstance().contactsSectionsDict.get(ContactsController.getInstance().sortedContactsSectionsArray.get(section - 1));
ContactsController.Contact contact = arr.get(position);
if (divider != null) {
if (position == arr.size() - 1) {
divider.setVisibility(View.INVISIBLE);
} else {
divider.setVisibility(View.VISIBLE);
}
}
if (contact.first_name != null && contact.last_name != null) {
textView.setText(contact.first_name + " " + contact.last_name);
} else if (contact.first_name != null && contact.last_name == null) {
textView.setText(contact.first_name);
} else {
textView.setText(contact.last_name);
}
return convertView;
}
@Override
public int getItemViewType(int section, int position) {
if (usersAsSections) {
if (section < ContactsController.getInstance().sortedUsersSectionsArray.size()) {
return 0;
}
} else if (section == 0) {
if (position == 0) {
return 2;
}
return 0;
}
return 1;
}
@Override
public int getItemViewTypeCount() {
return 3;
}
@Override
public int getSectionHeaderViewType(int section) {
if (usersAsSections) {
if (section < ContactsController.getInstance().sortedUsersSectionsArray.size()) {
return 1;
}
} else if (section == 0) {
return 0;
}
return 1;
}
@Override
public int getSectionHeaderViewTypeCount() {
return 2;
}
@Override
public View getSectionHeaderView(int section, View convertView, ViewGroup parent) {
if (usersAsSections) {
if (section < ContactsController.getInstance().sortedUsersSectionsArray.size()) {
if (convertView == null) {
convertView = new SettingsSectionLayout(mContext);
convertView.setBackgroundColor(0xffffffff);
}
((SettingsSectionLayout) convertView).setText(ContactsController.getInstance().sortedUsersSectionsArray.get(section));
return convertView;
}
} else {
if (section == 0) {
if (convertView == null) {
LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = li.inflate(R.layout.empty_layout, parent, false);
}
return convertView;
}
}
if (convertView == null) {
convertView = new SettingsSectionLayout(mContext);
convertView.setBackgroundColor(0xffffffff);
}
((SettingsSectionLayout) convertView).setText(ContactsController.getInstance().sortedContactsSectionsArray.get(section - 1));
return convertView;
}
}

View File

@ -0,0 +1,271 @@
/*
* This is the source code of Telegram for Android v. 1.3.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-2014.
*/
package org.telegram.ui.Adapters;
import android.content.Context;
import android.os.Build;
import android.view.View;
import android.view.ViewGroup;
import org.telegram.android.AndroidUtilities;
import org.telegram.android.LocaleController;
import org.telegram.messenger.TLRPC;
import org.telegram.android.ContactsController;
import org.telegram.android.MessagesController;
import org.telegram.messenger.R;
import org.telegram.ui.AnimationCompat.ViewProxy;
import org.telegram.ui.Cells.DividerCell;
import org.telegram.ui.Cells.GreySectionCell;
import org.telegram.ui.Cells.LetterSectionCell;
import org.telegram.ui.Cells.TextCell;
import org.telegram.ui.Cells.UserCell;
import java.util.ArrayList;
import java.util.HashMap;
public class ContactsAdapter extends BaseSectionsAdapter {
private Context mContext;
private boolean onlyUsers;
private boolean needPhonebook;
private HashMap<Integer, TLRPC.User> ignoreUsers;
private HashMap<Integer, ?> checkedMap;
private boolean scrolling;
public ContactsAdapter(Context context, boolean arg1, boolean arg2, HashMap<Integer, TLRPC.User> arg3) {
mContext = context;
onlyUsers = arg1;
needPhonebook = arg2;
ignoreUsers = arg3;
}
public void setCheckedMap(HashMap<Integer, ?> map) {
checkedMap = map;
}
public void setIsScrolling(boolean value) {
scrolling = value;
}
@Override
public Object getItem(int section, int position) {
if (onlyUsers) {
if (section < ContactsController.getInstance().sortedUsersSectionsArray.size()) {
ArrayList<TLRPC.TL_contact> arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section));
if (position < arr.size()) {
return MessagesController.getInstance().getUser(arr.get(position).user_id);
}
}
return null;
} else {
if (section == 0) {
return null;
} else {
if (section - 1 < ContactsController.getInstance().sortedUsersSectionsArray.size()) {
ArrayList<TLRPC.TL_contact> arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section - 1));
if (position < arr.size()) {
return MessagesController.getInstance().getUser(arr.get(position).user_id);
}
return null;
}
}
}
if (needPhonebook) {
return ContactsController.getInstance().phoneBookContacts.get(position);
}
return null;
}
@Override
public boolean isRowEnabled(int section, int row) {
if (onlyUsers) {
ArrayList<TLRPC.TL_contact> arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section));
return row < arr.size();
} else {
if (section == 0) {
if (needPhonebook) {
if (row == 1) {
return false;
}
} else {
if (row == 3) {
return false;
}
}
return true;
} else if (section - 1 < ContactsController.getInstance().sortedUsersSectionsArray.size()) {
ArrayList<TLRPC.TL_contact> arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section - 1));
return row < arr.size();
}
}
return true;
}
@Override
public int getSectionCount() {
int count = ContactsController.getInstance().sortedUsersSectionsArray.size();
if (!onlyUsers) {
count++;
}
if (needPhonebook) {
count++;
}
return count;
}
@Override
public int getCountForSection(int section) {
if (onlyUsers) {
if (section < ContactsController.getInstance().sortedUsersSectionsArray.size()) {
ArrayList<TLRPC.TL_contact> arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section));
int count = arr.size();
if (section != (ContactsController.getInstance().sortedUsersSectionsArray.size() - 1) || needPhonebook) {
count++;
}
return count;
}
} else {
if (section == 0) {
if (needPhonebook) {
return 2;
} else {
return 4;
}
} else if (section - 1 < ContactsController.getInstance().sortedUsersSectionsArray.size()) {
ArrayList<TLRPC.TL_contact> arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section - 1));
int count = arr.size();
if (section - 1 != (ContactsController.getInstance().sortedUsersSectionsArray.size() - 1) || needPhonebook) {
count++;
}
return count;
}
}
if (needPhonebook) {
return ContactsController.getInstance().phoneBookContacts.size();
}
return 0;
}
@Override
public View getSectionHeaderView(int section, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = new LetterSectionCell(mContext);
}
if (onlyUsers) {
if (section < ContactsController.getInstance().sortedUsersSectionsArray.size()) {
((LetterSectionCell) convertView).setLetter(ContactsController.getInstance().sortedUsersSectionsArray.get(section));
} else {
((LetterSectionCell) convertView).setLetter("");
}
} else {
if (section == 0) {
((LetterSectionCell) convertView).setLetter("");
} else if (section - 1 < ContactsController.getInstance().sortedUsersSectionsArray.size()) {
((LetterSectionCell) convertView).setLetter(ContactsController.getInstance().sortedUsersSectionsArray.get(section - 1));
} else {
((LetterSectionCell) convertView).setLetter("");
}
}
return convertView;
}
@Override
public View getItemView(int section, int position, View convertView, ViewGroup parent) {
int type = getItemViewType(section, position);
if (type == 4) {
if (convertView == null) {
convertView = new DividerCell(mContext);
convertView.setPadding(AndroidUtilities.dp(LocaleController.isRTL ? 28 : 72), 0, AndroidUtilities.dp(LocaleController.isRTL ? 72 : 28), 0);
}
} else if (type == 3) {
if (convertView == null) {
convertView = new GreySectionCell(mContext);
((GreySectionCell) convertView).setText(LocaleController.getString("Contacts", R.string.Contacts).toUpperCase());
}
} else if (type == 2) {
if (convertView == null) {
convertView = new TextCell(mContext);
}
TextCell actionCell = (TextCell) convertView;
if (needPhonebook) {
actionCell.setTextAndIcon(LocaleController.getString("InviteFriends", R.string.InviteFriends), R.drawable.menu_invite);
} else {
if (position == 0) {
actionCell.setTextAndIcon(LocaleController.getString("NewGroup", R.string.NewGroup), R.drawable.menu_newgroup);
} else if (position == 1) {
actionCell.setTextAndIcon(LocaleController.getString("NewSecretChat", R.string.NewSecretChat), R.drawable.menu_secret);
} else if (position == 2) {
actionCell.setTextAndIcon(LocaleController.getString("NewBroadcastList", R.string.NewBroadcastList), R.drawable.menu_broadcast);
}
}
} else if (type == 1) {
if (convertView == null) {
convertView = new TextCell(mContext);
}
ContactsController.Contact contact = ContactsController.getInstance().phoneBookContacts.get(position);
if (contact.first_name != null && contact.last_name != null) {
((TextCell) convertView).setText(contact.first_name + " " + contact.last_name);
} else if (contact.first_name != null && contact.last_name == null) {
((TextCell) convertView).setText(contact.first_name);
} else {
((TextCell) convertView).setText(contact.last_name);
}
} else if (type == 0) {
if (convertView == null) {
convertView = new UserCell(mContext, 58);
((UserCell) convertView).setStatusColors(0xffa8a8a8, 0xff3b84c0);
}
ArrayList<TLRPC.TL_contact> arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section - (onlyUsers ? 0 : 1)));
TLRPC.User user = MessagesController.getInstance().getUser(arr.get(position).user_id);
((UserCell)convertView).setData(user, null, null, 0);
if (checkedMap != null) {
((UserCell) convertView).setChecked(checkedMap.containsKey(user.id), !scrolling && Build.VERSION.SDK_INT > 10);
}
if (ignoreUsers != null) {
if (ignoreUsers.containsKey(user.id)) {
ViewProxy.setAlpha(convertView, 0.5f);
} else {
ViewProxy.setAlpha(convertView, 1.0f);
}
}
}
return convertView;
}
@Override
public int getItemViewType(int section, int position) {
if (onlyUsers) {
ArrayList<TLRPC.TL_contact> arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section));
return position < arr.size() ? 0 : 4;
} else {
if (section == 0) {
if (needPhonebook) {
if (position == 1) {
return 3;
}
} else {
if (position == 3) {
return 3;
}
}
return 2;
} else if (section - 1 < ContactsController.getInstance().sortedUsersSectionsArray.size()) {
ArrayList<TLRPC.TL_contact> arr = ContactsController.getInstance().usersSectionsDict.get(ContactsController.getInstance().sortedUsersSectionsArray.get(section - 1));
return position < arr.size() ? 0 : 4;
}
}
return 1;
}
@Override
public int getViewTypeCount() {
return 5;
}
}

View File

@ -22,29 +22,47 @@ import org.telegram.messenger.FileLog;
import org.telegram.android.MessagesController;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.Utilities;
import org.telegram.ui.Cells.ChatOrUserCell;
import org.telegram.ui.Views.SettingsSectionLayout;
import org.telegram.ui.Cells.GreySectionCell;
import org.telegram.ui.Cells.ProfileSearchCell;
import org.telegram.ui.Cells.UserCell;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;
public class ContactsActivitySearchAdapter extends BaseContactsSearchAdapter {
public class ContactsSearchAdapter extends BaseContactsSearchAdapter {
private Context mContext;
private HashMap<Integer, TLRPC.User> ignoreUsers;
private ArrayList<TLRPC.User> searchResult;
private ArrayList<CharSequence> searchResultNames;
private ArrayList<TLRPC.User> searchResult = new ArrayList<TLRPC.User>();
private ArrayList<CharSequence> searchResultNames = new ArrayList<CharSequence>();
private HashMap<Integer, ?> checkedMap;
private Timer searchTimer;
private boolean allowUsernameSearch;
private boolean useUserCell;
public ContactsActivitySearchAdapter(Context context, HashMap<Integer, TLRPC.User> arg1, boolean usernameSearch) {
public ContactsSearchAdapter(Context context, HashMap<Integer, TLRPC.User> arg1, boolean usernameSearch) {
mContext = context;
ignoreUsers = arg1;
allowUsernameSearch = usernameSearch;
}
public void setCheckedMap(HashMap<Integer, ?> map) {
checkedMap = map;
}
public void setUseUserCell(boolean value) {
useUserCell = value;
}
public void searchDialogs(final String query) {
try {
if (searchTimer != null) {
searchTimer.cancel();
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
if (query == null) {
searchResult.clear();
searchResultNames.clear();
@ -53,13 +71,6 @@ public class ContactsActivitySearchAdapter extends BaseContactsSearchAdapter {
}
notifyDataSetChanged();
} else {
try {
if (searchTimer != null) {
searchTimer.cancel();
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
searchTimer = new Timer();
searchTimer.schedule(new TimerTask() {
@Override
@ -77,7 +88,7 @@ public class ContactsActivitySearchAdapter extends BaseContactsSearchAdapter {
}
private void processSearch(final String query) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (allowUsernameSearch) {
@ -130,7 +141,7 @@ public class ContactsActivitySearchAdapter extends BaseContactsSearchAdapter {
}
private void updateSearchResults(final ArrayList<TLRPC.User> users, final ArrayList<CharSequence> names) {
AndroidUtilities.RunOnUIThread(new Runnable() {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
searchResult = users;
@ -197,16 +208,21 @@ public class ContactsActivitySearchAdapter extends BaseContactsSearchAdapter {
public View getView(int i, View view, ViewGroup viewGroup) {
if (i == searchResult.size()) {
if (view == null) {
view = new SettingsSectionLayout(mContext);
((SettingsSectionLayout) view).setText(LocaleController.getString("GlobalSearch", R.string.GlobalSearch));
view = new GreySectionCell(mContext);
((GreySectionCell) view).setText(LocaleController.getString("GlobalSearch", R.string.GlobalSearch));
}
} else {
if (view == null) {
view = new ChatOrUserCell(mContext);
((ChatOrUserCell) view).usePadding = false;
if (useUserCell) {
view = new UserCell(mContext, 1);
if (checkedMap != null) {
((UserCell) view).setChecked(false, false);
}
} else {
view = new ProfileSearchCell(mContext);
}
}
((ChatOrUserCell) view).useSeparator = (i != getCount() - 1 && i != searchResult.size() - 1);
TLRPC.User user = getItem(i);
if (user != null) {
CharSequence username = null;
@ -221,20 +237,27 @@ public class ContactsActivitySearchAdapter extends BaseContactsSearchAdapter {
}
} else if (i > searchResult.size() && user.username != null) {
try {
username = Html.fromHtml(String.format("<font color=\"#357aa8\">@%s</font>%s", user.username.substring(0, lastFoundUsername.length()), user.username.substring(lastFoundUsername.length())));
username = Html.fromHtml(String.format("<font color=\"#4d83b3\">@%s</font>%s", user.username.substring(0, lastFoundUsername.length()), user.username.substring(lastFoundUsername.length())));
} catch (Exception e) {
username = user.username;
FileLog.e("tmessages", e);
}
}
((ChatOrUserCell) view).setData(user, null, null, name, username);
if (useUserCell) {
((UserCell) view).setData(user, name, username, 0);
if (checkedMap != null) {
((UserCell) view).setChecked(checkedMap.containsKey(user.id), false);
}
} else {
((ProfileSearchCell) view).setData(user, null, null, name, username);
((ProfileSearchCell) view).useSeparator = (i != getCount() - 1 && i != searchResult.size() - 1);
if (ignoreUsers != null) {
if (ignoreUsers.containsKey(user.id)) {
((ChatOrUserCell) view).drawAlpha = 0.5f;
((ProfileSearchCell) view).drawAlpha = 0.5f;
} else {
((ChatOrUserCell) view).drawAlpha = 1.0f;
((ProfileSearchCell) view).drawAlpha = 1.0f;
}
}
}
}

View File

@ -0,0 +1,165 @@
/*
* This is the source code of Telegram for Android v. 1.7.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-2014.
*/
package org.telegram.ui.Adapters;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import org.telegram.android.AndroidUtilities;
import org.telegram.android.LocaleController;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.ui.Cells.DividerCell;
import org.telegram.ui.Cells.LetterSectionCell;
import org.telegram.ui.Cells.TextSettingsCell;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
public class CountryAdapter extends BaseSectionsAdapter {
public static class Country {
public String name;
public String code;
public String shortname;
}
private Context mContext;
private HashMap<String, ArrayList<Country>> countries = new HashMap<String, ArrayList<Country>>();
private ArrayList<String> sortedCountries = new ArrayList<String>();
public CountryAdapter(Context context) {
mContext = context;
try {
InputStream stream = ApplicationLoader.applicationContext.getResources().getAssets().open("countries.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
String line;
while ((line = reader.readLine()) != null) {
String[] args = line.split(";");
Country c = new Country();
c.name = args[2];
c.code = args[0];
c.shortname = args[1];
String n = c.name.substring(0, 1).toUpperCase();
ArrayList<Country> arr = countries.get(n);
if (arr == null) {
arr = new ArrayList<Country>();
countries.put(n, arr);
sortedCountries.add(n);
}
arr.add(c);
}
reader.close();
stream.close();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
Collections.sort(sortedCountries, new Comparator<String>() {
@Override
public int compare(String lhs, String rhs) {
return lhs.compareTo(rhs);
}
});
for (ArrayList<Country> arr : countries.values()) {
Collections.sort(arr, new Comparator<Country>() {
@Override
public int compare(Country country, Country country2) {
return country.name.compareTo(country2.name);
}
});
}
}
public HashMap<String, ArrayList<Country>> getCountries() {
return countries;
}
@Override
public Country getItem(int section, int position) {
if (section < 0 || section >= sortedCountries.size()) {
return null;
}
ArrayList<Country> arr = countries.get(sortedCountries.get(section));
if (position < 0 || position >= arr.size()) {
return null;
}
return arr.get(position);
}
@Override
public boolean isRowEnabled(int section, int row) {
ArrayList<Country> arr = countries.get(sortedCountries.get(section));
return row < arr.size();
}
@Override
public int getSectionCount() {
return sortedCountries.size();
}
@Override
public int getCountForSection(int section) {
int count = countries.get(sortedCountries.get(section)).size();
if (section != sortedCountries.size() - 1) {
count++;
}
return count;
}
@Override
public View getSectionHeaderView(int section, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = new LetterSectionCell(mContext);
((LetterSectionCell) convertView).setCellHeight(AndroidUtilities.dp(48));
}
((LetterSectionCell) convertView).setLetter(sortedCountries.get(section).toUpperCase());
return convertView;
}
@Override
public View getItemView(int section, int position, View convertView, ViewGroup parent) {
int type = getItemViewType(section, position);
if (type == 1) {
if (convertView == null) {
convertView = new DividerCell(mContext);
convertView.setPadding(AndroidUtilities.dp(LocaleController.isRTL ? 24 : 72), 0, AndroidUtilities.dp(LocaleController.isRTL ? 72 : 24), 0);
}
} else if (type == 0) {
if (convertView == null) {
convertView = new TextSettingsCell(mContext);
convertView.setPadding(AndroidUtilities.dp(LocaleController.isRTL ? 16 : 54), 0, AndroidUtilities.dp(LocaleController.isRTL ? 54 : 16), 0);
}
ArrayList<Country> arr = countries.get(sortedCountries.get(section));
Country c = arr.get(position);
((TextSettingsCell) convertView).setTextAndValue(c.name, "+" + c.code, false);
}
return convertView;
}
@Override
public int getItemViewType(int section, int position) {
ArrayList<Country> arr = countries.get(sortedCountries.get(section));
return position < arr.size() ? 0 : 1;
}
@Override
public int getViewTypeCount() {
return 2;
}
}

View File

@ -0,0 +1,165 @@
/*
* This is the source code of Telegram for Android v. 1.7.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-2014.
*/
package org.telegram.ui.Adapters;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import org.telegram.android.AndroidUtilities;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.Utilities;
import org.telegram.ui.Adapters.CountryAdapter.Country;
import org.telegram.ui.Cells.TextSettingsCell;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;
public class CountrySearchAdapter extends BaseFragmentAdapter {
private Context mContext;
private Timer searchTimer;
private ArrayList<CountryAdapter.Country> searchResult;
private HashMap<String, ArrayList<Country>> countries;
public CountrySearchAdapter(Context context, HashMap<String, ArrayList<Country>> countries) {
mContext = context;
this.countries = countries;
}
public void search(final String query) {
if (query == null) {
searchResult = null;
} else {
try {
if (searchTimer != null) {
searchTimer.cancel();
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
searchTimer = new Timer();
searchTimer.schedule(new TimerTask() {
@Override
public void run() {
try {
searchTimer.cancel();
searchTimer = null;
} catch (Exception e) {
FileLog.e("tmessages", e);
}
processSearch(query);
}
}, 100, 300);
}
}
private void processSearch(final String query) {
Utilities.searchQueue.postRunnable(new Runnable() {
@Override
public void run() {
String q = query.trim().toLowerCase();
if (q.length() == 0) {
updateSearchResults(new ArrayList<Country>());
return;
}
long time = System.currentTimeMillis();
ArrayList<Country> resultArray = new ArrayList<Country>();
String n = query.substring(0, 1);
ArrayList<Country> arr = countries.get(n.toUpperCase());
if (arr != null) {
for (Country c : arr) {
if (c.name.toLowerCase().startsWith(query)) {
resultArray.add(c);
}
}
}
updateSearchResults(resultArray);
}
});
}
private void updateSearchResults(final ArrayList<Country> arrCounties) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
searchResult = arrCounties;
notifyDataSetChanged();
}
});
}
@Override
public boolean areAllItemsEnabled() {
return true;
}
@Override
public boolean isEnabled(int i) {
return true;
}
@Override
public int getCount() {
if (searchResult == null) {
return 0;
}
return searchResult.size();
}
@Override
public Country getItem(int i) {
if (i < 0 || i >= searchResult.size()) {
return null;
}
return searchResult.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
if (view == null) {
view = new TextSettingsCell(mContext);
}
Country c = searchResult.get(i);
((TextSettingsCell) view).setTextAndValue(c.name, "+" + c.code, i != searchResult.size() - 1);
return view;
}
@Override
public int getItemViewType(int i) {
return 0;
}
@Override
public int getViewTypeCount() {
return 1;
}
@Override
public boolean isEmpty() {
return searchResult == null || searchResult.size() == 0;
}
}

View File

@ -0,0 +1,153 @@
/*
* This is the source code of Telegram for Android v. 1.7.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-2014.
*/
package org.telegram.ui.Adapters;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import org.telegram.android.AndroidUtilities;
import org.telegram.android.MessageObject;
import org.telegram.android.MessagesController;
import org.telegram.messenger.TLRPC;
import org.telegram.ui.Cells.DialogCell;
import org.telegram.ui.Cells.LoadingCell;
public class DialogsAdapter extends BaseFragmentAdapter {
private Context mContext;
private boolean serverOnly;
private long openedDialogId;
public DialogsAdapter(Context context, boolean onlyFromServer) {
mContext = context;
serverOnly = onlyFromServer;
}
public void setOpenedDialogId(long id) {
openedDialogId = id;
}
@Override
public boolean areAllItemsEnabled() {
return true;
}
@Override
public boolean isEnabled(int i) {
return true;
}
@Override
public int getCount() {
int count;
if (serverOnly) {
count = MessagesController.getInstance().dialogsServerOnly.size();
} else {
count = MessagesController.getInstance().dialogs.size();
}
if (count == 0 && MessagesController.getInstance().loadingDialogs) {
return 0;
}
if (!MessagesController.getInstance().dialogsEndReached) {
count++;
}
return count;
}
@Override
public TLRPC.TL_dialog getItem(int i) {
if (serverOnly) {
if (i < 0 || i >= MessagesController.getInstance().dialogsServerOnly.size()) {
return null;
}
return MessagesController.getInstance().dialogsServerOnly.get(i);
} else {
if (i < 0 || i >= MessagesController.getInstance().dialogs.size()) {
return null;
}
return MessagesController.getInstance().dialogs.get(i);
}
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
int type = getItemViewType(i);
if (type == 1) {
if (view == null) {
view = new LoadingCell(mContext);
}
} else if (type == 0) {
if (view == null) {
view = new DialogCell(mContext);
}
((DialogCell) view).useSeparator = (i != getCount() - 1);
TLRPC.TL_dialog dialog = null;
if (serverOnly) {
dialog = MessagesController.getInstance().dialogsServerOnly.get(i);
} else {
dialog = MessagesController.getInstance().dialogs.get(i);
if (AndroidUtilities.isTablet()) {
if (dialog.id == openedDialogId) {
view.setBackgroundColor(0x0f000000);
} else {
view.setBackgroundColor(0);
}
}
}
MessageObject message = MessagesController.getInstance().dialogMessage.get(dialog.top_message);
((DialogCell) view).setDialog(dialog.id, message, true, dialog.last_message_date, dialog.unread_count);
}
return view;
}
@Override
public int getItemViewType(int i) {
if (serverOnly && i == MessagesController.getInstance().dialogsServerOnly.size() || !serverOnly && i == MessagesController.getInstance().dialogs.size()) {
return 1;
}
return 0;
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public boolean isEmpty() {
if (MessagesController.getInstance().loadingDialogs && MessagesController.getInstance().dialogs.isEmpty()) {
return true;
}
int count;
if (serverOnly) {
count = MessagesController.getInstance().dialogsServerOnly.size();
} else {
count = MessagesController.getInstance().dialogs.size();
}
if (count == 0 && MessagesController.getInstance().loadingDialogs) {
return true;
}
if (!MessagesController.getInstance().dialogsEndReached) {
count++;
}
return count == 0;
}
}

View File

@ -0,0 +1,631 @@
/*
* This is the source code of Telegram for Android v. 1.7.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-2014.
*/
package org.telegram.ui.Adapters;
import android.content.Context;
import android.text.Html;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import org.telegram.SQLite.SQLiteCursor;
import org.telegram.android.AndroidUtilities;
import org.telegram.android.ContactsController;
import org.telegram.android.LocaleController;
import org.telegram.android.MessageObject;
import org.telegram.android.MessagesController;
import org.telegram.android.MessagesStorage;
import org.telegram.messenger.ByteBufferDesc;
import org.telegram.messenger.ConnectionsManager;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.R;
import org.telegram.messenger.RPCRequest;
import org.telegram.messenger.TLClassStore;
import org.telegram.messenger.TLObject;
import org.telegram.messenger.TLRPC;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.Utilities;
import org.telegram.ui.Cells.DialogCell;
import org.telegram.ui.Cells.GreySectionCell;
import org.telegram.ui.Cells.LoadingCell;
import org.telegram.ui.Cells.ProfileSearchCell;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
public class DialogsSearchAdapter extends BaseContactsSearchAdapter {
private Context mContext;
private Timer searchTimer;
private ArrayList<TLObject> searchResult = new ArrayList<TLObject>();
private ArrayList<CharSequence> searchResultNames = new ArrayList<CharSequence>();
private ArrayList<MessageObject> searchResultMessages = new ArrayList<MessageObject>();
private String lastSearchText;
private long reqId = 0;
private int lastReqId;
private MessagesActivitySearchAdapterDelegate delegate;
private boolean needMessagesSearch;
private boolean messagesSearchEndReached;
private String lastMessagesSearchString;
private int lastSearchId = 0;
private class DialogSearchResult {
public TLObject object;
public int date;
public CharSequence name;
}
public static interface MessagesActivitySearchAdapterDelegate {
public abstract void searchStateChanged(boolean searching);
}
public DialogsSearchAdapter(Context context, boolean messagesSearch) {
mContext = context;
needMessagesSearch = messagesSearch;
}
public void setDelegate(MessagesActivitySearchAdapterDelegate delegate) {
this.delegate = delegate;
}
public boolean isMessagesSearchEndReached() {
return messagesSearchEndReached;
}
public void loadMoreSearchMessages() {
searchMessagesInternal(lastMessagesSearchString);
}
private void searchMessagesInternal(final String query) {
if (!needMessagesSearch) {
return;
}
if (reqId != 0) {
ConnectionsManager.getInstance().cancelRpc(reqId, true);
reqId = 0;
}
if (query == null || query.length() == 0) {
searchResultMessages.clear();
lastReqId = 0;
lastMessagesSearchString = null;
notifyDataSetChanged();
if (delegate != null) {
delegate.searchStateChanged(false);
}
return;
}
final TLRPC.TL_messages_search req = new TLRPC.TL_messages_search();
req.limit = 20;
req.peer = new TLRPC.TL_inputPeerEmpty();
req.q = query;
if (lastMessagesSearchString != null && query.equals(lastMessagesSearchString) && !searchResultMessages.isEmpty()) {
req.max_id = searchResultMessages.get(searchResultMessages.size() - 1).messageOwner.id;
}
lastMessagesSearchString = query;
req.filter = new TLRPC.TL_inputMessagesFilterEmpty();
final int currentReqId = ++lastReqId;
if (delegate != null) {
delegate.searchStateChanged(true);
}
reqId = ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() {
@Override
public void run(final TLObject response, final TLRPC.TL_error error) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (currentReqId == lastReqId) {
if (error == null) {
TLRPC.messages_Messages res = (TLRPC.messages_Messages) response;
MessagesStorage.getInstance().putUsersAndChats(res.users, res.chats, true, true);
MessagesController.getInstance().putUsers(res.users, false);
MessagesController.getInstance().putChats(res.chats, false);
if (req.max_id == 0) {
searchResultMessages.clear();
}
for (TLRPC.Message message : res.messages) {
searchResultMessages.add(new MessageObject(message, null, 0));
}
messagesSearchEndReached = res.messages.size() != 20;
notifyDataSetChanged();
}
}
if (delegate != null) {
delegate.searchStateChanged(false);
}
reqId = 0;
}
});
}
}, true, RPCRequest.RPCRequestClassGeneric | RPCRequest.RPCRequestClassFailOnServerErrors);
}
private void searchDialogsInternal(final String query, final boolean serverOnly, final int searchId) {
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
@Override
public void run() {
try {
String q = query.trim().toLowerCase();
if (q.length() == 0) {
lastSearchId = -1;
updateSearchResults(new ArrayList<TLObject>(), new ArrayList<CharSequence>(), new ArrayList<TLRPC.User>(), lastSearchId);
return;
}
ArrayList<Integer> usersToLoad = new ArrayList<Integer>();
ArrayList<Integer> chatsToLoad = new ArrayList<Integer>();
ArrayList<Integer> encryptedToLoad = new ArrayList<Integer>();
ArrayList<TLRPC.User> encUsers = new ArrayList<TLRPC.User>();
int resultCount = 0;
HashMap<Long, DialogSearchResult> dialogsResult = new HashMap<Long, DialogSearchResult>();
SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized(String.format(Locale.US, "SELECT did, date FROM dialogs ORDER BY date DESC LIMIT 200"));
while (cursor.next()) {
long id = cursor.longValue(0);
DialogSearchResult dialogSearchResult = new DialogSearchResult();
dialogSearchResult.date = cursor.intValue(1);
dialogsResult.put(id, dialogSearchResult);
int lower_id = (int)id;
int high_id = (int)(id >> 32);
if (lower_id != 0) {
if (high_id == 1) {
if (!serverOnly && !chatsToLoad.contains(lower_id)) {
chatsToLoad.add(lower_id);
}
} else {
if (lower_id > 0) {
if (!usersToLoad.contains(lower_id)) {
usersToLoad.add(lower_id);
}
} else {
if (!chatsToLoad.contains(-lower_id)) {
chatsToLoad.add(-lower_id);
}
}
}
} else if (!serverOnly) {
if (!encryptedToLoad.contains(high_id)) {
encryptedToLoad.add(high_id);
}
}
}
cursor.dispose();
if (!usersToLoad.isEmpty()) {
cursor = MessagesStorage.getInstance().getDatabase().queryFinalized(String.format(Locale.US, "SELECT data, status, name FROM users WHERE uid IN(%s)", TextUtils.join(",", usersToLoad)));
while (cursor.next()) {
String name = cursor.stringValue(2);
String username = null;
int usernamePos = name.lastIndexOf(";;;");
if (usernamePos != -1) {
username = name.substring(usernamePos + 3);
}
int found = 0;
if (name.startsWith(q) || name.contains(" " + q)) {
found = 1;
} else if (username != null && username.startsWith(q)) {
found = 2;
}
if (found != 0) {
ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) {
TLRPC.User user = (TLRPC.User) TLClassStore.Instance().TLdeserialize(data, data.readInt32());
if (user.id != UserConfig.getClientUserId()) {
DialogSearchResult dialogSearchResult = dialogsResult.get((long)user.id);
if (user.status != null) {
user.status.expires = cursor.intValue(1);
}
if (found == 1) {
dialogSearchResult.name = Utilities.generateSearchName(user.first_name, user.last_name, q);
} else {
dialogSearchResult.name = Utilities.generateSearchName("@" + user.username, null, "@" + q);
}
dialogSearchResult.object = user;
resultCount++;
}
}
MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data);
}
}
cursor.dispose();
}
if (!chatsToLoad.isEmpty()) {
cursor = MessagesStorage.getInstance().getDatabase().queryFinalized(String.format(Locale.US, "SELECT data, name FROM chats WHERE uid IN(%s)", TextUtils.join(",", chatsToLoad)));
while (cursor.next()) {
String name = cursor.stringValue(1);
String[] args = name.split(" ");
if (name.startsWith(q) || name.contains(" " + q)) {
ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) {
TLRPC.Chat chat = (TLRPC.Chat) TLClassStore.Instance().TLdeserialize(data, data.readInt32());
long dialog_id;
if (chat.id > 0) {
dialog_id = -chat.id;
} else {
dialog_id = AndroidUtilities.makeBroadcastId(chat.id);
}
DialogSearchResult dialogSearchResult = dialogsResult.get(dialog_id);
dialogSearchResult.name = Utilities.generateSearchName(chat.title, null, q);
dialogSearchResult.object = chat;
resultCount++;
}
MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data);
}
}
cursor.dispose();
}
if (!encryptedToLoad.isEmpty()) {
cursor = MessagesStorage.getInstance().getDatabase().queryFinalized(String.format(Locale.US, "SELECT q.data, u.name, q.user, q.g, q.authkey, q.ttl, u.data, u.status, q.layer, q.seq_in, q.seq_out, q.use_count, q.exchange_id, q.key_date, q.fprint, q.fauthkey, q.khash FROM enc_chats as q INNER JOIN users as u ON q.user = u.uid WHERE q.uid IN(%s)", TextUtils.join(",", encryptedToLoad)));
while (cursor.next()) {
String name = cursor.stringValue(1);
String username = null;
int usernamePos = name.lastIndexOf(";;;");
if (usernamePos != -1) {
username = name.substring(usernamePos + 2);
}
int found = 0;
if (name.startsWith(q) || name.contains(" " + q)) {
found = 1;
} else if (username != null && username.startsWith(q)) {
found = 2;
}
if (found != 0) {
ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0));
ByteBufferDesc data2 = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(6));
if (data != null && cursor.byteBufferValue(0, data.buffer) != 0 && cursor.byteBufferValue(6, data2.buffer) != 0) {
TLRPC.EncryptedChat chat = (TLRPC.EncryptedChat) TLClassStore.Instance().TLdeserialize(data, data.readInt32());
DialogSearchResult dialogSearchResult = dialogsResult.get((long)chat.id << 32);
chat.user_id = cursor.intValue(2);
chat.a_or_b = cursor.byteArrayValue(3);
chat.auth_key = cursor.byteArrayValue(4);
chat.ttl = cursor.intValue(5);
chat.layer = cursor.intValue(8);
chat.seq_in = cursor.intValue(9);
chat.seq_out = cursor.intValue(10);
int use_count = cursor.intValue(11);
chat.key_use_count_in = (short)(use_count >> 16);
chat.key_use_count_out = (short)(use_count);
chat.exchange_id = cursor.longValue(12);
chat.key_create_date = cursor.intValue(13);
chat.future_key_fingerprint = cursor.longValue(14);
chat.future_auth_key = cursor.byteArrayValue(15);
chat.key_hash = cursor.byteArrayValue(16);
TLRPC.User user = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data2, data2.readInt32());
if (user.status != null) {
user.status.expires = cursor.intValue(7);
}
if (found == 1) {
dialogSearchResult.name = Html.fromHtml("<font color=\"#00a60e\">" + ContactsController.formatName(user.first_name, user.last_name) + "</font>");
} else {
dialogSearchResult.name = Utilities.generateSearchName("@" + user.username, null, "@" + q);
}
dialogSearchResult.object = chat;
encUsers.add(user);
resultCount++;
}
MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data);
MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data2);
}
}
cursor.dispose();
}
ArrayList<DialogSearchResult> searchResults = new ArrayList<DialogSearchResult>(resultCount);
for (DialogSearchResult dialogSearchResult : dialogsResult.values()) {
if (dialogSearchResult.object != null && dialogSearchResult.name != null) {
searchResults.add(dialogSearchResult);
}
}
Collections.sort(searchResults, new Comparator<DialogSearchResult>() {
@Override
public int compare(DialogSearchResult lhs, DialogSearchResult rhs) {
if (lhs.date < rhs.date) {
return 1;
} else if (lhs.date > rhs.date) {
return -1;
}
return 0;
}
});
ArrayList<TLObject> resultArray = new ArrayList<TLObject>();
ArrayList<CharSequence> resultArrayNames = new ArrayList<CharSequence>();
for (DialogSearchResult dialogSearchResult : searchResults) {
resultArray.add(dialogSearchResult.object);
resultArrayNames.add(dialogSearchResult.name);
}
cursor = MessagesStorage.getInstance().getDatabase().queryFinalized("SELECT u.data, u.status, u.name, u.uid FROM users as u INNER JOIN contacts as c ON u.uid = c.uid");
while (cursor.next()) {
int uid = cursor.intValue(3);
if (dialogsResult.containsKey((long)uid)) {
continue;
}
String name = cursor.stringValue(2);
String username = null;
int usernamePos = name.lastIndexOf(";;;");
if (usernamePos != -1) {
username = name.substring(usernamePos + 3);
}
int found = 0;
if (name.startsWith(q) || name.contains(" " + q)) {
found = 1;
} else if (username != null && username.startsWith(q)) {
found = 2;
}
if (found != 0) {
ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) {
TLRPC.User user = (TLRPC.User) TLClassStore.Instance().TLdeserialize(data, data.readInt32());
if (user.id != UserConfig.getClientUserId()) {
if (user.status != null) {
user.status.expires = cursor.intValue(1);
}
if (found == 1) {
resultArrayNames.add(Utilities.generateSearchName(user.first_name, user.last_name, q));
} else {
resultArrayNames.add(Utilities.generateSearchName("@" + user.username, null, "@" + q));
}
resultArray.add(user);
}
}
MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data);
}
}
cursor.dispose();
updateSearchResults(resultArray, resultArrayNames, encUsers, searchId);
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
});
}
private void updateSearchResults(final ArrayList<TLObject> result, final ArrayList<CharSequence> names, final ArrayList<TLRPC.User> encUsers, final int searchId) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
if (searchId != lastSearchId) {
return;
}
for (TLObject obj : result) {
if (obj instanceof TLRPC.User) {
TLRPC.User user = (TLRPC.User) obj;
MessagesController.getInstance().putUser(user, true);
} else if (obj instanceof TLRPC.Chat) {
TLRPC.Chat chat = (TLRPC.Chat) obj;
MessagesController.getInstance().putChat(chat, true);
} else if (obj instanceof TLRPC.EncryptedChat) {
TLRPC.EncryptedChat chat = (TLRPC.EncryptedChat) obj;
MessagesController.getInstance().putEncryptedChat(chat, true);
}
}
for (TLRPC.User user : encUsers) {
MessagesController.getInstance().putUser(user, true);
}
searchResult = result;
searchResultNames = names;
notifyDataSetChanged();
}
});
}
public String getLastSearchText() {
return lastSearchText;
}
public boolean isGlobalSearch(int i) {
return i > searchResult.size() && i <= globalSearch.size() + searchResult.size();
}
public void searchDialogs(final String query, final boolean serverOnly) {
if (query != null && lastSearchText != null && query.equals(lastSearchText)) {
return;
}
try {
if (searchTimer != null) {
searchTimer.cancel();
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
if (query == null || query.length() == 0) {
searchResult.clear();
searchResultNames.clear();
searchMessagesInternal(null);
queryServerSearch(null);
notifyDataSetChanged();
} else {
final int searchId = ++lastSearchId;
searchTimer = new Timer();
searchTimer.schedule(new TimerTask() {
@Override
public void run() {
try {
searchTimer.cancel();
searchTimer = null;
} catch (Exception e) {
FileLog.e("tmessages", e);
}
searchDialogsInternal(query, serverOnly, searchId);
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
queryServerSearch(query);
searchMessagesInternal(query);
}
});
}
}, 200, 300);
}
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
@Override
public boolean isEnabled(int i) {
return i != searchResult.size() && i != searchResult.size() + (globalSearch.isEmpty() ? 0 : globalSearch.size() + 1);
}
@Override
public int getCount() {
int count = searchResult.size();
int globalCount = globalSearch.size();
int messagesCount = searchResultMessages.size();
if (globalCount != 0) {
count += globalCount + 1;
}
if (messagesCount != 0) {
count += messagesCount + 1 + (messagesSearchEndReached ? 0 : 1);
}
return count;
}
@Override
public Object getItem(int i) {
int localCount = searchResult.size();
int globalCount = globalSearch.isEmpty() ? 0 : globalSearch.size() + 1;
int messagesCount = searchResultMessages.isEmpty() ? 0 : searchResultMessages.size() + 1;
if (i >= 0 && i < localCount) {
return searchResult.get(i);
} else if (i > localCount && i < globalCount + localCount) {
return globalSearch.get(i - localCount - 1);
} else if (i > globalCount + localCount && i < globalCount + localCount + messagesCount) {
return searchResultMessages.get(i - localCount - globalCount - 1);
}
return null;
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
int type = getItemViewType(i);
if (type == 1) {
if (view == null) {
view = new GreySectionCell(mContext);
}
if (!globalSearch.isEmpty() && i == searchResult.size()) {
((GreySectionCell) view).setText(LocaleController.getString("GlobalSearch", R.string.GlobalSearch));
} else {
((GreySectionCell) view).setText(LocaleController.getString("SearchMessages", R.string.SearchMessages));
}
} else if (type == 0) {
if (view == null) {
view = new ProfileSearchCell(mContext);
}
TLRPC.User user = null;
TLRPC.Chat chat = null;
TLRPC.EncryptedChat encryptedChat = null;
int localCount = searchResult.size();
int globalCount = globalSearch.isEmpty() ? 0 : globalSearch.size() + 1;
((ProfileSearchCell) view).useSeparator = (i != getCount() - 1 && i != localCount - 1 && i != localCount + globalCount - 1);
Object obj = getItem(i);
if (obj instanceof TLRPC.User) {
user = MessagesController.getInstance().getUser(((TLRPC.User) obj).id);
if (user == null) {
user = (TLRPC.User) obj;
}
} else if (obj instanceof TLRPC.Chat) {
chat = MessagesController.getInstance().getChat(((TLRPC.Chat) obj).id);
} else if (obj instanceof TLRPC.EncryptedChat) {
encryptedChat = MessagesController.getInstance().getEncryptedChat(((TLRPC.EncryptedChat) obj).id);
user = MessagesController.getInstance().getUser(encryptedChat.user_id);
}
CharSequence username = null;
CharSequence name = null;
if (i < searchResult.size()) {
name = searchResultNames.get(i);
if (name != null && user != null && user.username != null && user.username.length() > 0) {
if (name.toString().startsWith("@" + user.username)) {
username = name;
name = null;
}
}
} else if (i > searchResult.size() && user != null && user.username != null) {
try {
username = Html.fromHtml(String.format("<font color=\"#4d83b3\">@%s</font>%s", user.username.substring(0, lastFoundUsername.length()), user.username.substring(lastFoundUsername.length())));
} catch (Exception e) {
username = user.username;
FileLog.e("tmessages", e);
}
}
((ProfileSearchCell) view).setData(user, chat, encryptedChat, name, username);
} else if (type == 2) {
if (view == null) {
view = new DialogCell(mContext);
}
((DialogCell) view).useSeparator = (i != getCount() - 1);
MessageObject messageObject = (MessageObject)getItem(i);
((DialogCell) view).setDialog(messageObject.getDialogId(), messageObject, false, messageObject.messageOwner.date, 0);
} else if (type == 3) {
if (view == null) {
view = new LoadingCell(mContext);
}
}
return view;
}
@Override
public int getItemViewType(int i) {
int localCount = searchResult.size();
int globalCount = globalSearch.isEmpty() ? 0 : globalSearch.size() + 1;
int messagesCount = searchResultMessages.isEmpty() ? 0 : searchResultMessages.size() + 1;
if (i >= 0 && i < localCount || i > localCount && i < globalCount + localCount) {
return 0;
} else if (i > globalCount + localCount && i < globalCount + localCount + messagesCount) {
return 2;
} else if (messagesCount != 0 && i == globalCount + localCount + messagesCount) {
return 3;
}
return 1;
}
@Override
public int getViewTypeCount() {
return 4;
}
@Override
public boolean isEmpty() {
return searchResult.isEmpty() && globalSearch.isEmpty() && searchResultMessages.isEmpty();
}
}

View File

@ -0,0 +1,125 @@
/*
* This is the source code of Telegram for Android v. 1.7.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-2014.
*/
package org.telegram.ui.Adapters;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import org.telegram.android.LocaleController;
import org.telegram.android.MessagesController;
import org.telegram.messenger.R;
import org.telegram.messenger.UserConfig;
import org.telegram.ui.Cells.DrawerActionCell;
import org.telegram.ui.Cells.DividerCell;
import org.telegram.ui.Cells.EmptyCell;
import org.telegram.ui.Cells.DrawerProfileCell;
public class DrawerLayoutAdapter extends BaseAdapter {
private Context mContext;
public DrawerLayoutAdapter(Context context) {
mContext = context;
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
@Override
public boolean isEnabled(int i) {
return !(i == 0 || i == 1 || i == 5);
}
@Override
public int getCount() {
return UserConfig.isClientActivated() ? 10 : 0;
}
@Override
public Object getItem(int i) {
return null;
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
int type = getItemViewType(i);
if (type == 0) {
if (view == null) {
view = new DrawerProfileCell(mContext);
}
((DrawerProfileCell) view).setUser(MessagesController.getInstance().getUser(UserConfig.getClientUserId()));
} else if (type == 1) {
if (view == null) {
view = new EmptyCell(mContext, 8);
}
} else if (type == 2) {
if (view == null) {
view = new DividerCell(mContext);
}
} else if (type == 3) {
if (view == null) {
view = new DrawerActionCell(mContext);
}
DrawerActionCell actionCell = (DrawerActionCell) view;
if (i == 2) {
actionCell.setTextAndIcon(LocaleController.getString("NewGroup", R.string.NewGroup), R.drawable.menu_newgroup);
} else if (i == 3) {
actionCell.setTextAndIcon(LocaleController.getString("NewSecretChat", R.string.NewSecretChat), R.drawable.menu_secret);
} else if (i == 4) {
actionCell.setTextAndIcon(LocaleController.getString("NewBroadcastList", R.string.NewBroadcastList), R.drawable.menu_broadcast);
} else if (i == 6) {
actionCell.setTextAndIcon(LocaleController.getString("Contacts", R.string.Contacts), R.drawable.menu_contacts);
} else if (i == 7) {
actionCell.setTextAndIcon(LocaleController.getString("InviteFriends", R.string.InviteFriends), R.drawable.menu_invite);
} else if (i == 8) {
actionCell.setTextAndIcon(LocaleController.getString("Settings", R.string.Settings), R.drawable.menu_settings);
} else if (i == 9) {
actionCell.setTextAndIcon(LocaleController.getString("TelegramFaq", R.string.TelegramFaq), R.drawable.menu_help);
}
}
return view;
}
@Override
public int getItemViewType(int i) {
if (i == 0) {
return 0;
} else if (i == 1) {
return 1;
} else if (i == 5) {
return 2;
}
return 3;
}
@Override
public int getViewTypeCount() {
return 4;
}
@Override
public boolean isEmpty() {
return !UserConfig.isClientActivated();
}
}

View File

@ -0,0 +1,191 @@
/*
* 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.ui.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 static interface AnimatorListener {
void onAnimationStart(Animator10 animation);
void onAnimationEnd(Animator10 animation);
void onAnimationCancel(Animator10 animation);
void onAnimationRepeat(Animator10 animation);
}
public static interface AnimatorPauseListener {
void onAnimationPause(Animator10 animation);
void onAnimationResume(Animator10 animation);
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.ui.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) {
}
}

View File

@ -0,0 +1,705 @@
/*
* 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.ui.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<Animator10>();
private HashMap<Animator10, Node> mNodeMap = new HashMap<Animator10, Node>();
private ArrayList<Node> mNodes = new ArrayList<Node>();
private ArrayList<Node> mSortedNodes = new ArrayList<Node>();
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<Animator10>();
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<AnimatorListener>(oldListeners);
for (AnimatorListener listener : clonedListeners) {
if (listener instanceof DependencyListener ||
listener instanceof AnimatorSetListener) {
node.animation.removeListener(listener);
}
}
}
}
final ArrayList<Node> nodesToStart = new ArrayList<Node>();
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<Animator10>();
anim.mNodeMap = new HashMap<Animator10, Node>();
anim.mNodes = new ArrayList<Node>();
anim.mSortedNodes = new ArrayList<Node>();
HashMap<Node, Node> nodeCloneMap = new HashMap<Node, Node>();
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<AnimatorListener>();
}
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<Node>();
int numNodes = mNodes.size();
for (Node node : mNodes) {
if (node.dependencies == null || node.dependencies.size() == 0) {
roots.add(node);
}
}
ArrayList<Node> tmpRoots = new ArrayList<Node>();
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<Node>();
}
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<Dependency>();
nodeDependencies = new ArrayList<Node>();
}
dependencies.add(dependency);
if (!nodeDependencies.contains(dependency.node)) {
nodeDependencies.add(dependency.node);
}
Node dependencyNode = dependency.node;
if (dependencyNode.nodeDependents == null) {
dependencyNode.nodeDependents = new ArrayList<Node>();
}
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;
}
}
}

View File

@ -0,0 +1,24 @@
/*
* 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.ui.Animation;
public class FloatEvaluator implements TypeEvaluator<Number> {
public Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
}

View File

@ -0,0 +1,114 @@
/*
* 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.ui.Animation;
import android.view.animation.Interpolator;
import org.telegram.ui.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();
}
}

View File

@ -0,0 +1,30 @@
/*
* 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.ui.Animation;
public abstract class FloatProperty10<T> extends Property<T, Float> {
public FloatProperty10(String name) {
super(Float.class, name);
}
public abstract void setValue(T object, float value);
@Override
final public void set(T object, Float value) {
setValue(object, value.floatValue());
}
}

View File

@ -0,0 +1,24 @@
/*
* 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.ui.Animation;
public class IntEvaluator implements TypeEvaluator<Integer> {
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}

View File

@ -0,0 +1,114 @@
/*
* 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.ui.Animation;
import android.view.animation.Interpolator;
import org.telegram.ui.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();
}
}

View File

@ -0,0 +1,30 @@
/*
* 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.ui.Animation;
public abstract class IntProperty<T> extends Property<T, Integer> {
public IntProperty(String name) {
super(Integer.class, name);
}
public abstract void setValue(T object, int value);
@Override
final public void set(T object, Integer value) {
setValue(object, value.intValue());
}
}

View File

@ -0,0 +1,187 @@
/*
* 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.ui.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;
}
}
}

View File

@ -0,0 +1,200 @@
/*
* 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.ui.Animation;
import java.util.ArrayList;
import java.util.Arrays;
import android.util.Log;
import android.view.animation.Interpolator;
import org.telegram.ui.Animation.Keyframe.IntKeyframe;
import org.telegram.ui.Animation.Keyframe.FloatKeyframe;
import org.telegram.ui.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;
}
}

View File

@ -0,0 +1,24 @@
/*
* 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.ui.Animation;
public class NoSuchPropertyException extends RuntimeException {
public NoSuchPropertyException(String s) {
super(s);
}
}

View File

@ -0,0 +1,488 @@
/*
* 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.ui.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();
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.ui.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;
}
}

View File

@ -0,0 +1,556 @@
/*
* 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.ui.Animation;
import java.lang.reflect.InvocationTargetException;
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 (NoSuchMethodException e) {
try {
returnVal = targetClass.getDeclaredMethod(methodName);
returnVal.setAccessible(true);
} catch (NoSuchMethodException 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 (NoSuchMethodException e) {
try {
returnVal = targetClass.getDeclaredMethod(methodName, args);
returnVal.setAccessible(true);
mValueType = typeVariant;
return returnVal;
} catch (NoSuchMethodException 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 (ClassCastException 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 (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException 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 (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException 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 (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException 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 (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException 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 (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
@Override
void setupSetter(Class targetClass) {
if (mProperty != null) {
return;
}
super.setupSetter(targetClass);
}
}
}

View File

@ -0,0 +1,182 @@
/*
* 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.ui.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);
}
}

View File

@ -0,0 +1,21 @@
/*
* 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.ui.Animation;
public interface TypeEvaluator<T> {
public T evaluate(float fraction, T startValue, T endValue);
}

View File

@ -0,0 +1,675 @@
/*
* 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.ui.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.android.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 static 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;
}
}

View File

@ -0,0 +1,349 @@
/*
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.ui.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<View, View10>();
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>(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);
}
}
}

View File

@ -0,0 +1,112 @@
/*
* This is the source code of Telegram for Android v. 1.7.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-2014.
*/
package org.telegram.ui.AnimationCompat;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import org.telegram.ui.Animation.Animator10;
import org.telegram.ui.Animation.AnimatorListenerAdapter10;
import org.telegram.ui.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) {
}
}

View File

@ -0,0 +1,128 @@
/*
* This is the source code of Telegram for Android v. 1.7.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-2014.
*/
package org.telegram.ui.AnimationCompat;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.view.animation.Interpolator;
import org.telegram.ui.Animation.Animator10;
import org.telegram.ui.Animation.AnimatorListenerAdapter10;
import org.telegram.ui.Animation.AnimatorSet10;
import org.telegram.ui.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<Animator10>();
for (Object obj : items) {
animators.add((Animator10)obj);
}
((AnimatorSet10) animatorSet).playTogether(animators);
} else {
ArrayList<Animator> animators = new ArrayList<Animator>();
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 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;
}
}

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